"""
主窗口界面
"""

from _io import BufferedWriter
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os
import sys
import time
import shutil
import datetime
from typing import Any, Optional, Dict, List

# 添加src目录到Python路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from modules.face_detection import detect_faces
from modules.face_features import extract_face_features
from modules.face_matching import recognize_face
from modules.face_database import add_face_to_database, remove_face_from_database, list_faces_in_database
from utils.data_storage import load_face_database, save_face_database
from gui.api_database_tab import create_api_database_tab
from gui.app_library_tab import create_app_library_tab

# 导入OpenCV和PIL库
try:
    import cv2
    # 类型提示
    from typing import TYPE_CHECKING
    if TYPE_CHECKING:
        import numpy as np
        # 使用字符串注释方式避免直接导入
        Mat = 'cv2.Mat'
except ImportError:
    cv2 = None

try:
    from PIL import Image, ImageDraw, ImageFont
except ImportError:
    Image = ImageDraw = ImageFont = None

# 导入face_recognition库
try:
    import face_recognition
except ImportError:
    face_recognition = None

# 导入numpy库
try:
    import numpy as np
except ImportError:
    np = None


class FaceRecognitionGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("人脸识别系统")
        self.root.geometry("900x700")
        
        # 初始化_window_data属性
        self._window_data = {}
        
        # 设置样式
        self.setup_styles()
        
        # 用于跟踪已打开的预览窗口
        self.preview_windows = {}
        
        # 用于跟踪编辑窗口中的选中图片状态
        self.selected_image_indices = {}
        
        # 人脸数据库
        self.database_file = "data/face_database.json"
        self.face_database = load_face_database(self.database_file)
        
        # API配置管理器
        from config.api_config import APIConfigManager
        self.api_config_manager = APIConfigManager("config/api_config.yaml")
        self.api_config = self.api_config_manager.config
        
        # 检查并创建必要的目录
        self._check_and_create_directories()
        
        # 创建界面
        self.create_widgets()
        self.load_api_config_to_ui()
        
        # 用于跟踪摄像头线程
        self.video_thread = None
        self.video_running = False

    def setup_styles(self):
        """设置界面样式"""
        style = ttk.Style()
        
        # 设置主题
        try:
            style.theme_use('clam')
        except tk.TclError:
            pass  # 如果主题不可用，则使用默认主题
        
        # 配置按钮样式
        style.configure("Accent.TButton", 
                       foreground="white", 
                       background="#4a86e8", 
                       font=("Arial", 10, "bold"),
                       padding=6)
        style.map("Accent.TButton",
                 background=[("active", "#3a76d8")])
        
        # 配置标签框架样式
        style.configure("TLabelframe.Label", 
                       font=("Arial", 11, "bold"),
                       foreground="#2c3e50")
        
        # 配置选项卡样式
        style.configure("TNotebook.Tab", 
                       font=("Arial", 10),
                       padding=(10, 5))
        
    def create_widgets(self):
        # 创建选项卡
        tab_control = ttk.Notebook(self.root)
        
        # 人脸检测选项卡
        self.detection_tab = ttk.Frame(tab_control)
        tab_control.add(self.detection_tab, text="人脸检测")
        
        # 人脸识别选项卡
        self.recognition_tab = ttk.Frame(tab_control)
        tab_control.add(self.recognition_tab, text="人脸识别")
        
        # 摄像头人脸识别选项卡
        self.camera_recognition_tab = ttk.Frame(tab_control)
        tab_control.add(self.camera_recognition_tab, text="摄像头识别")
        
        # 人脸库管理选项卡
        self.database_tab = ttk.Frame(tab_control)
        tab_control.add(self.database_tab, text="人脸库管理")
        
        # 设置选项卡
        self.settings_tab = ttk.Frame(tab_control)
        tab_control.add(self.settings_tab, text="设置")
        
        # API库选项卡
        self.api_database_tab = create_api_database_tab(tab_control, self)
        
        # 应用库选项卡
        self.app_library_tab = create_app_library_tab(tab_control, self)
        
        tab_control.pack(expand=1, fill="both")
        
        # 创建各选项卡的内容
        self.create_detection_tab()
        self.create_recognition_tab()
        self.create_camera_recognition_tab()  # 新增的摄像头识别选项卡
        self.create_database_tab()
        self.create_settings_tab()
        
    def create_detection_tab(self):
        # 人脸检测选项卡内容
        frame = ttk.Frame(self.detection_tab, padding="20")
        frame.grid(row=0, column=0, sticky="ewns")
        self.detection_tab.columnconfigure(0, weight=1)
        self.detection_tab.rowconfigure(0, weight=1)
        frame.columnconfigure(0, weight=1)  # 修改这一行，确保内容横向撑满
        
        # 标题
        ttk.Label(frame, text="人脸检测", font=("Arial", 16, "bold")).grid(row=0, column=0, columnspan=3, pady=(0, 20), sticky="w")
        
        # 图像选择区域
        input_frame = ttk.LabelFrame(frame, text="图像选择", padding="10")
        input_frame.grid(row=1, column=0, columnspan=3, sticky="ew", pady=(0, 20))
        input_frame.columnconfigure(1, weight=1)
        
        ttk.Label(input_frame, text="选择图像:").grid(row=0, column=0, sticky="w", pady=5, padx=(0, 10))
        self.image_path_var = tk.StringVar()
        ttk.Entry(input_frame, textvariable=self.image_path_var).grid(row=0, column=1, sticky="ew", pady=5)
        ttk.Button(input_frame, text="浏览", command=self.browse_image).grid(row=0, column=2, padx=(10, 0), pady=5)
        
        # 检测按钮
        button_frame = ttk.Frame(frame)
        button_frame.grid(row=2, column=0, columnspan=3, pady=(0, 20))
        ttk.Button(button_frame, text="检测人脸", command=self.detect_faces, style="Accent.TButton").pack()
        
        # 结果显示区域
        result_frame = ttk.LabelFrame(frame, text="检测结果", padding="10")
        result_frame.grid(row=3, column=0, columnspan=3, sticky="nsew", pady=(0, 10))
        frame.rowconfigure(3, weight=1)
        result_frame.columnconfigure(0, weight=1)
        result_frame.rowconfigure(0, weight=1)
        
        self.detection_result = tk.Text(result_frame, height=15, width=70)
        self.detection_result.grid(row=0, column=0, sticky="nsew")
        scrollbar = ttk.Scrollbar(result_frame, orient="vertical", command=self.detection_result.yview)
        scrollbar.grid(row=0, column=1, sticky="ns")
        self.detection_result.configure(yscrollcommand=scrollbar.set)
        
    def create_recognition_tab(self):
        # 人脸识别选项卡内容
        frame = ttk.Frame(self.recognition_tab, padding="20")
        frame.grid(row=0, column=0, sticky="ewns")
        self.recognition_tab.columnconfigure(0, weight=1)
        self.recognition_tab.rowconfigure(0, weight=1)
        frame.columnconfigure(0, weight=1)  # 修改这一行，确保内容横向撑满
        
        # 标题
        ttk.Label(frame, text="人脸识别", font=("Arial", 16, "bold")).grid(row=0, column=0, columnspan=3, pady=(0, 20), sticky="w")
        
        # 图像选择区域
        input_frame = ttk.LabelFrame(frame, text="图像选择", padding="10")
        input_frame.grid(row=1, column=0, columnspan=3, sticky="ew", pady=(0, 20))
        input_frame.columnconfigure(1, weight=1)
        
        ttk.Label(input_frame, text="选择图像:").grid(row=0, column=0, sticky="w", pady=5, padx=(0, 10))
        self.recognition_image_path_var = tk.StringVar()
        ttk.Entry(input_frame, textvariable=self.recognition_image_path_var).grid(row=0, column=1, sticky="ew", pady=5)
        ttk.Button(input_frame, text="浏览", command=self.browse_recognition_image).grid(row=0, column=2, padx=(10, 0), pady=5)
        
        # 识别按钮
        button_frame = ttk.Frame(frame)
        button_frame.grid(row=2, column=0, columnspan=3, pady=(0, 20))
        ttk.Button(button_frame, text="识别人脸", command=self.recognize_faces, style="Accent.TButton").pack()
        
        # 结果显示区域
        result_frame = ttk.LabelFrame(frame, text="识别结果", padding="10")
        result_frame.grid(row=3, column=0, columnspan=3, sticky="nsew", pady=(0, 10))
        frame.rowconfigure(3, weight=1)
        result_frame.columnconfigure(0, weight=1)
        result_frame.rowconfigure(0, weight=1)
        
        self.recognition_result = tk.Text(result_frame, height=15, width=70)
        self.recognition_result.grid(row=0, column=0, sticky="nsew")
        scrollbar = ttk.Scrollbar(result_frame, orient="vertical", command=self.recognition_result.yview)
        scrollbar.grid(row=0, column=1, sticky="ns")
        self.recognition_result.configure(yscrollcommand=scrollbar.set)
        
    def create_camera_recognition_tab(self):
        """创建摄像头人脸识别选项卡"""
        frame = ttk.Frame(self.camera_recognition_tab, padding="20")
        frame.grid(row=0, column=0, sticky="ewns")
        self.camera_recognition_tab.columnconfigure(0, weight=1)
        self.camera_recognition_tab.rowconfigure(0, weight=1)
        frame.columnconfigure(0, weight=1)  # 添加这一行，确保内容横向撑满
        
        # 标题
        ttk.Label(frame, text="摄像头人脸识别", font=("Arial", 16, "bold")).grid(row=0, column=0, columnspan=3, pady=(0, 20), sticky="w")
        
        # 摄像头控制区域
        control_frame = ttk.LabelFrame(frame, text="摄像头控制", padding="10")
        control_frame.grid(row=1, column=0, columnspan=3, sticky="ew", pady=(0, 20))
        control_frame.columnconfigure(0, weight=1)  # 让状态标签可以扩展
        control_frame.columnconfigure(1, weight=0)  # 启动按钮不扩展
        control_frame.columnconfigure(2, weight=0)  # 停止按钮不扩展
        
        self.camera_status_var = tk.StringVar(value="摄像头未启动")
        ttk.Label(control_frame, textvariable=self.camera_status_var).grid(row=0, column=0, sticky="w", pady=5)
        
        self.start_camera_btn = ttk.Button(control_frame, text="启动摄像头", command=self.start_camera_recognition, style="Accent.TButton")
        self.start_camera_btn.grid(row=0, column=1, padx=(10, 5), pady=5)
        
        self.stop_camera_btn = ttk.Button(control_frame, text="停止摄像头", command=self.stop_camera_recognition, state="disabled")
        self.stop_camera_btn.grid(row=0, column=2, padx=(5, 0), pady=5)
        
        # 静默模式控制
        self.silent_mode_var = tk.BooleanVar(value=False)
        ttk.Checkbutton(control_frame, text="静默模式（不显示预览窗口）", variable=self.silent_mode_var).grid(row=1, column=0, columnspan=3, sticky="w", pady=5)
        
        # 识别结果显示区域
        result_frame = ttk.LabelFrame(frame, text="识别结果", padding="10")
        result_frame.grid(row=2, column=0, columnspan=3, sticky="nsew", pady=(0, 10))
        frame.rowconfigure(2, weight=1)
        result_frame.columnconfigure(0, weight=1)
        result_frame.rowconfigure(0, weight=1)
        
        self.camera_result = tk.Text(result_frame, height=20, width=70)
        self.camera_result.grid(row=0, column=0, sticky="nsew")
        scrollbar = ttk.Scrollbar(result_frame, orient="vertical", command=self.camera_result.yview)
        scrollbar.grid(row=0, column=1, sticky="ns")
        self.camera_result.configure(yscrollcommand=scrollbar.set)
        
        # 识别设置区域
        settings_frame = ttk.LabelFrame(frame, text="识别设置", padding="10")
        settings_frame.grid(row=3, column=0, columnspan=3, sticky="ew", pady=(0, 10))
        settings_frame.columnconfigure(1, weight=1)  # 确保滑块可以水平扩展
        
        ttk.Label(settings_frame, text="匹配容差:").grid(row=0, column=0, sticky="w", pady=5, padx=(0, 10))
        self.tolerance_var = tk.DoubleVar(value=0.4)  # 降低容差值以提高相似度要求
        tolerance_scale = ttk.Scale(settings_frame, from_=0.1, to=1.0, variable=self.tolerance_var, orient="horizontal")
        tolerance_scale.grid(row=0, column=1, sticky="ew", pady=5)
        
        self.tolerance_label = ttk.Label(settings_frame, text=f"{self.tolerance_var.get():.1f}")
        self.tolerance_label.grid(row=0, column=2, sticky="w", pady=5, padx=(10, 0))
        
        # 绑定滑块事件
        tolerance_scale.configure(command=self.update_tolerance_label)
        
        # 添加说明文本
        ttk.Label(settings_frame, text="数值越小，匹配要求越严格，相似度越高", foreground="gray").grid(row=1, column=0, columnspan=3, sticky="w", pady=(5, 0))
        
    def update_tolerance_label(self, value):
        """更新容差标签显示"""
        self.tolerance_label.config(text=f"{float(value):.1f}")
        
    def start_camera_recognition(self):
        """启动摄像头人脸识别"""
        if self.video_running:
            return
            
        # 检查是否有已知人脸数据
        if not self.face_database:
            messagebox.showwarning("提示", "人脸数据库为空，请先添加人脸数据再进行识别。", parent=self.root)
            return
            
        self.video_running = True
        self.camera_status_var.set("摄像头运行中...")
        self.start_camera_btn.config(state="disabled")
        self.stop_camera_btn.config(state="normal")
        self.camera_result.delete(1.0, tk.END)
        self.camera_result.insert(tk.END, "摄像头人脸识别已启动...\n\n")
        
        # 在新线程中运行摄像头识别
        import threading
        self.video_thread = threading.Thread(target=self._camera_recognition_loop, daemon=True)
        self.video_thread.start()
        
    def stop_camera_recognition(self):
        """停止摄像头人脸识别"""
        self.video_running = False
        self.camera_status_var.set("摄像头已停止")
        self.start_camera_btn.config(state="normal")
        self.stop_camera_btn.config(state="disabled")
        self.camera_result.insert(tk.END, "\n摄像头人脸识别已停止。\n")
        
    def _camera_recognition_loop(self):
        """摄像头人脸识别循环"""
        try:
            # 检查OpenCV是否可用
            if cv2 is None:
                raise ImportError("OpenCV库未安装，请确保已安装opencv-python库")
            
            # 初始化摄像头
            video_capture = getattr(cv2, 'VideoCapture')(0)
            
            # 设置摄像头参数以提高图像质量
            video_capture.set(getattr(cv2, 'CAP_PROP_FRAME_WIDTH'), 1280)
            video_capture.set(getattr(cv2, 'CAP_PROP_FRAME_HEIGHT'), 720)
            video_capture.set(getattr(cv2, 'CAP_PROP_FPS'), 30)
            
            recognition_count = 0
            
            # 尝试加载支持中文的字体
            font = None
            if Image is not None and ImageDraw is not None and ImageFont is not None:
                # 尝试使用系统字体，如果失败则使用默认字体
                try:
                    # Windows系统字体
                    font_path = "C:/Windows/Fonts/simsun.ttc"  # 宋体
                    if not os.path.exists(font_path):
                        # 尝试其他常见中文字体路径
                        font_path = "C:/Windows/Fonts/msyh.ttc"  # 微软雅黑
                    if not os.path.exists(font_path):
                        font_path = "C:/Windows/Fonts/simhei.ttf"  # 黑体
                    
                    # 如果找不到系统字体，使用默认字体
                    if os.path.exists(font_path):
                        font = ImageFont.truetype(font_path, 20)
                    else:
                        font = ImageFont.load_default()
                except:
                    font = ImageFont.load_default()
            
            while self.video_running:
                # 获取一帧视频
                ret, frame = video_capture.read()
                if not ret:
                    continue
                
                # 调整帧大小以提高处理速度，但保持合理的质量
                small_frame = getattr(cv2, 'resize')(frame, (0, 0), fx=0.5, fy=0.5)
                
                # 转换BGR图片到RGB图片 (face_recognition使用RGB)
                # 使用更明确的颜色空间转换方法
                rgb_small_frame = getattr(cv2, 'cvtColor')(small_frame, getattr(cv2, 'COLOR_BGR2RGB'))
                
                # 查找当前帧中所有人脸的位置和编码
                # 检查face_recognition是否可用
                if face_recognition is None:
                    raise ImportError("face_recognition库未安装，请确保已安装face-recognition库")
                # 使用更高质量的模型和更多采样点来提高准确性
                face_locations = face_recognition.face_locations(rgb_small_frame, model="hog")
                # 使用默认的采样点数来提高编码质量
                face_encodings = face_recognition.face_encodings(rgb_small_frame, face_locations)
                
                face_names = []
                # 检查人脸数据库是否为空
                if len(self.face_database) > 0:
                    for face_encoding in face_encodings:
                        name = "未知"
                        # 使用高级识别算法
                        from modules.face_matching import recognize_face_advanced
                        person_name, confidence = recognize_face_advanced(face_encoding, self.face_database)
                        
                        # 格式化显示名称和置信度
                        if person_name != "未知":
                            name = f"{person_name} ({confidence:.3f})"
                        else:
                            # 即使不匹配也显示最接近的结果和置信度
                            name = f"未知 - 相似度: {confidence:.3f}"
                        
                        face_names.append(name)
                else:
                    # 如果人脸数据库为空，只显示检测到的人脸数量
                    face_names = [f"未知 (未识别人脸)"] * len(face_encodings)
                
                # 显示结果（仅在非静默模式下）
                if not self.silent_mode_var.get():
                    for (top, right, bottom, left), name in zip(face_locations, face_names):
                        # 由于我们检测的是缩小的帧，需要将坐标放大回原始帧大小
                        top *= 2
                        right *= 2
                        bottom *= 2
                        left *= 2
                        
                        # 在人脸周围画框
                        getattr(cv2, 'rectangle')(frame, (left, top), (right, bottom), (0, 0, 255), 2)
                        
                        # 在人脸下方绘制标签
                        getattr(cv2, 'rectangle')(frame, (left, bottom - 35), (right, bottom), (0, 0, 255), getattr(cv2, 'FILLED'))
                        
                        # 使用支持中文的字体绘制文本
                        if font is not None and Image is not None and ImageDraw is not None:
                            # 转换OpenCV图像到PIL图像
                            pil_image = Image.fromarray(getattr(cv2, 'cvtColor')(frame, getattr(cv2, 'COLOR_BGR2RGB')))
                            draw = ImageDraw.Draw(pil_image)
                            
                            # 绘制中文文本
                            draw.text((left + 6, bottom - 30), name, font=font, fill=(255, 255, 255))
                            
                            # 转换回OpenCV图像
                            import numpy as np
                            frame = getattr(cv2, 'cvtColor')(np.array(pil_image), getattr(cv2, 'COLOR_RGB2BGR'))
                        else:
                            # 如果无法使用PIL，回退到OpenCV默认方法（不支持中文）
                            getattr(cv2, 'putText')(frame, name, (left + 6, bottom - 6), getattr(cv2, 'FONT_HERSHEY_DUPLEX'), 0.6, (255, 255, 255), 1)
                    
                    # 显示结果图像
                    getattr(cv2, 'imshow')('Camera Recognition', frame)
                
                # 更新识别结果文本
                if face_names and recognition_count % 15 == 0:  # 每15帧更新一次文本
                    self.root.after(0, self._update_camera_result, face_names)
                
                recognition_count += 1
                
                # 按q退出（仅在非静默模式下）
                if not self.silent_mode_var.get() and getattr(cv2, 'waitKey')(1) & 0xFF == ord('q'):
                    break
            
            # 释放摄像头
            video_capture.release()
            # 仅在非静默模式下销毁窗口
            if not self.silent_mode_var.get():
                getattr(cv2, 'destroyAllWindows')()
            
        except ImportError as e:
            # 设置消息框的父窗口为编辑窗口，确保不会被遮挡
            messagebox.showerror("摄像头错误", f"缺少必要的库: {str(e)}\n请确保已安装opencv-python和Pillow库", parent=self.root)
        except Exception as e:
            messagebox.showerror("摄像头错误", f"摄像头功能出错: {str(e)}", parent=self.root)
            
    def _update_camera_result(self, face_names):
        """更新摄像头识别结果文本"""
        if not self.video_running:
            return
            
        import datetime
        timestamp = datetime.datetime.now().strftime("%H:%M:%S")
        self.camera_result.insert(tk.END, f"[{timestamp}] 检测到 {len(face_names)} 个人脸:\n")
        for i, name in enumerate(face_names):
            self.camera_result.insert(tk.END, f"  人脸 {i+1}: {name}\n")
        self.camera_result.see(tk.END)
        
    def _handle_camera_error(self, error_msg):
        """处理摄像头错误"""
        self.video_running = False
        self.camera_status_var.set("摄像头错误")
        self.start_camera_btn.config(state="normal")
        self.stop_camera_btn.config(state="disabled")
        messagebox.showerror("摄像头错误", f"摄像头识别出错: {error_msg}", parent=self.root)
        self.camera_result.insert(tk.END, f"\n错误: {error_msg}\n")

    def create_database_tab(self):
        # 人脸库管理选项卡内容
        frame = ttk.Frame(self.database_tab, padding="20")
        frame.grid(row=0, column=0, sticky="ewns")
        self.database_tab.columnconfigure(0, weight=1)
        self.database_tab.rowconfigure(0, weight=1)
        frame.columnconfigure(0, weight=1)  # 添加这一行，确保内容横向撑满
        
        # 标题
        ttk.Label(frame, text="人脸库管理", font=("Arial", 16, "bold")).grid(row=0, column=0, columnspan=3, pady=(0, 20), sticky="w")
        
        # 添加人脸区域
        add_frame = ttk.LabelFrame(frame, text="添加人脸到数据库", padding="10")
        add_frame.grid(row=1, column=0, columnspan=3, sticky="ew", pady=(0, 20))
        add_frame.columnconfigure(1, weight=1)
        
        ttk.Label(add_frame, text="人员姓名:").grid(row=0, column=0, sticky="w", pady=5, padx=(0, 10))
        self.person_name_var = tk.StringVar()
        ttk.Entry(add_frame, textvariable=self.person_name_var, width=30).grid(row=0, column=1, sticky="w", pady=5)
        
        ttk.Label(add_frame, text="图像路径:").grid(row=1, column=0, sticky="w", pady=5, padx=(0, 10))
        self.add_image_path_var = tk.StringVar()
        ttk.Entry(add_frame, textvariable=self.add_image_path_var).grid(row=1, column=1, sticky="ew", pady=5)
        ttk.Button(add_frame, text="浏览", command=self.browse_add_image).grid(row=1, column=2, padx=(10, 0), pady=5)
        
        # 按钮区域
        button_frame = ttk.Frame(add_frame)
        button_frame.grid(row=2, column=0, columnspan=3, pady=(10, 0))
        ttk.Button(button_frame, text="添加到人脸库", command=self.add_face_to_db, style="Accent.TButton").pack(side="left", padx=(0, 10))
        ttk.Button(button_frame, text="批量导入人脸照片", command=self.batch_import_faces).pack(side="left")
        
        # 人脸库管理区域
        manage_frame = ttk.LabelFrame(frame, text="人脸库列表", padding="10")
        manage_frame.grid(row=2, column=0, columnspan=3, sticky="nsew", pady=(0, 10))
        frame.rowconfigure(2, weight=1)
        manage_frame.columnconfigure(0, weight=1)
        manage_frame.rowconfigure(0, weight=1)
        
        # 左侧：人脸列表
        list_frame = ttk.Frame(manage_frame)
        list_frame.grid(row=0, column=0, sticky="nsew", pady=5)
        list_frame.columnconfigure(0, weight=1)
        list_frame.rowconfigure(0, weight=1)
        
        # 创建Treeview来显示人脸列表
        columns = ("name",)
        self.face_tree = ttk.Treeview(list_frame, columns=columns, show="tree", height=15)
        self.face_tree.heading("#0", text="人脸库")
        self.face_tree.column("#0", width=200)
        
        # 滚动条
        tree_scrollbar = ttk.Scrollbar(list_frame, orient="vertical", command=self.face_tree.yview)
        self.face_tree.configure(yscrollcommand=tree_scrollbar.set)
        
        self.face_tree.grid(row=0, column=0, sticky="nsew")
        tree_scrollbar.grid(row=0, column=1, sticky="ns")
        
        # 绑定双击事件
        self.face_tree.bind("<Double-1>", self.on_face_tree_double_click)
        
        # 右侧：操作按钮 - 使用更美观的布局
        button_frame = ttk.Frame(manage_frame)
        button_frame.grid(row=0, column=1, padx=(10, 0), sticky="nsew")
        manage_frame.rowconfigure(0, weight=1)  # 让按钮框架可以垂直扩展
        button_frame.columnconfigure(0, weight=1)  # 让按钮框架的列可以扩展
        
        # 创建一个内部框架来容纳按钮，使其在垂直方向上居中
        buttons_inner_frame = ttk.Frame(button_frame)
        buttons_inner_frame.pack(expand=True)
        
        ttk.Button(buttons_inner_frame, text="刷新", command=self.refresh_face_list).pack(fill="x", pady=5)
        # 移除了预览按钮，因为预览功能已集成到编辑窗口中
        ttk.Button(buttons_inner_frame, text="编辑", command=self.modify_selected_face).pack(fill="x", pady=5)
        ttk.Button(buttons_inner_frame, text="删除", command=self.delete_selected_face).pack(fill="x", pady=5)
        
        # 状态栏
        self.database_status_var = tk.StringVar()
        self.database_status_label = ttk.Label(frame, textvariable=self.database_status_var, 
                                              relief="sunken", anchor="w", padding=(5, 2))
        self.database_status_label.grid(row=3, column=0, columnspan=3, sticky="ew", pady=(5, 0))
        
        # 初始加载人脸列表
        self.refresh_face_list()
        
        # 绑定选择事件
        self.face_tree.bind("<<TreeviewSelect>>", self.on_face_selection_changed)
        
    def batch_import_faces(self):
        """批量导入人脸照片"""
        # 选择多个图像文件
        filenames = filedialog.askopenfilenames(
            parent=self.root,
            title="选择图像文件（可多选）",
            filetypes=[("图像文件", "*.jpg *.jpeg *.png *.bmp"), ("所有文件", "*.*")]
        )
        
        if not filenames:
            return
            
        # 创建进度窗口
        progress_window = tk.Toplevel(self.root)
        progress_window.title("批量导入进度")
        progress_window.geometry("400x300")
        progress_window.minsize(400, 300)  # 设置最小尺寸
        progress_window.transient(self.root)  # 设置为模态窗口
        progress_window.grab_set()  # 模态窗口
        
        # 居中显示进度窗口
        progress_window.update_idletasks()
        x = self.root.winfo_x() + (self.root.winfo_width() // 2) - (400 // 2)
        y = self.root.winfo_y() + (self.root.winfo_height() // 2) - (300 // 2)
        progress_window.geometry(f"400x300+{x}+{y}")
        
        # 标题标签
        title_label = ttk.Label(progress_window, text="正在批量导入人脸照片...", font=("Arial", 12))
        title_label.pack(pady=10)
        
        # 当前处理文件标签
        current_file_label = ttk.Label(progress_window, text="准备开始导入...", font=("Arial", 10))
        current_file_label.pack(pady=5)
        
        # 进度条
        progress = ttk.Progressbar(progress_window, orient="horizontal", length=300, mode="determinate")
        progress.pack(pady=10)
        progress["value"] = 0
        progress["maximum"] = len(filenames)
        
        # 进度标签
        progress_label = ttk.Label(progress_window, text=f"0 / {len(filenames)}")
        progress_label.pack(pady=5)
        
        # 结果文本框
        result_frame = ttk.Frame(progress_window)
        result_frame.pack(fill="both", expand=True, padx=20, pady=10)
        result_frame.columnconfigure(0, weight=1)
        result_frame.rowconfigure(0, weight=1)
        
        result_text = tk.Text(result_frame, height=8, width=50)
        result_text.grid(row=0, column=0, sticky="nsew")
        
        scrollbar = ttk.Scrollbar(result_frame, orient="vertical", command=result_text.yview)
        scrollbar.grid(row=0, column=1, sticky="ns")
        result_text.configure(yscrollcommand=scrollbar.set)
        
        # 更新进度窗口
        progress_window.update()
        
        success_count = 0
        fail_count = 0
        fail_list = []
        
        # 处理每个文件
        for i, filename in enumerate(filenames):
            # 更新当前处理文件标签
            current_file_label.config(text=f"正在处理: {os.path.basename(filename)}")
            progress_label.config(text=f"{i + 1} / {len(filenames)}")
            
            try:
                # 使用文件名基本名作为人员姓名
                person_name = os.path.splitext(os.path.basename(filename))[0]
                result_text.insert(tk.END, f"正在导入 {person_name} ...\n")
                result_text.see(tk.END)
                progress_window.update_idletasks()
                
                if add_face_to_database(person_name, filename, self.face_database):
                    success_count += 1
                    result_text.insert(tk.END, f"  ✓ 成功\n")
                else:
                    fail_count += 1
                    fail_list.append(filename)
                    result_text.insert(tk.END, f"  ✗ 失败\n")
                    print(f"导入失败: {filename}")
            except Exception as e:
                fail_count += 1
                fail_list.append(filename)
                result_text.insert(tk.END, f"  ✗ 错误: {str(e)}\n")
                print(f"导入失败: {filename}, 错误: {e}")
            
            # 更新进度条
            progress["value"] = i + 1
            progress_window.update_idletasks()
        
        # 保存数据库
        result_text.insert(tk.END, f"\n正在保存数据库...\n")
        progress_window.update_idletasks()
        
        save_result = save_face_database(self.face_database, self.database_file)
        if save_result:
            self.refresh_face_list()
            result_text.insert(tk.END, f"数据库保存成功\n")
        else:
            result_text.insert(tk.END, f"数据库保存失败\n")
        
        # 显示最终结果
        result_text.insert(tk.END, f"\n导入完成: 成功 {success_count} 个, 失败 {fail_count} 个\n")
        if fail_list:
            result_text.insert(tk.END, f"失败列表:\n")
            for fail_file in fail_list:
                result_text.insert(tk.END, f"  - {os.path.basename(fail_file)}\n")
        result_text.see(tk.END)
        progress_window.update_idletasks()
        
        # 等待一段时间让用户看到结果
        progress_window.after(2000, lambda: progress_window.destroy())  # 等待2秒后关闭窗口
        
        # 显示最终结果消息框
        if not save_result:
            messagebox.showerror("错误", "保存数据库失败", parent=self.root)
        elif fail_count > 0:
            messagebox.showinfo("导入结果", f"成功导入 {success_count} 张人脸照片，失败 {fail_count} 张。", parent=self.root)
        else:
            messagebox.showinfo("导入结果", f"成功导入 {success_count} 张人脸照片。", parent=self.root)
        
    def create_settings_tab(self):
        # 设置选项卡内容
        frame = ttk.Frame(self.settings_tab, padding="20")
        frame.grid(row=0, column=0, sticky="ewns")
        self.settings_tab.columnconfigure(0, weight=1)
        self.settings_tab.rowconfigure(0, weight=1)
        frame.columnconfigure(0, weight=1)  # 修改这一行，确保内容横向撑满
        
        # 标题
        ttk.Label(frame, text="系统设置", font=("Arial", 16, "bold")).grid(row=0, column=0, columnspan=3, pady=(0, 20), sticky="w")
        
        # 数据库设置区域
        db_frame = ttk.LabelFrame(frame, text="数据库设置", padding="10")
        db_frame.grid(row=1, column=0, columnspan=3, sticky="ew", pady=(0, 20))
        db_frame.columnconfigure(1, weight=1)
        
        ttk.Label(db_frame, text="数据库文件:").grid(row=0, column=0, sticky="w", pady=5, padx=(0, 10))
        self.database_file_var = tk.StringVar(value=self.database_file)
        ttk.Entry(db_frame, textvariable=self.database_file_var).grid(row=0, column=1, sticky="ew", pady=5)
        ttk.Button(db_frame, text="浏览", command=self.browse_database_file).grid(row=0, column=2, padx=(10, 0), pady=5)
        
        # API接口设置区域
        api_frame = ttk.LabelFrame(frame, text="API接口设置", padding="10")
        api_frame.grid(row=2, column=0, columnspan=3, sticky="ew", pady=(0, 20))
        api_frame.columnconfigure(1, weight=1)
        
        # Compreface API设置
        ttk.Label(api_frame, text="Compreface Base URL:").grid(row=0, column=0, sticky="w", pady=5, padx=(0, 10))
        self.compreface_base_url_var = tk.StringVar()
        ttk.Entry(api_frame, textvariable=self.compreface_base_url_var).grid(row=0, column=1, sticky="ew", pady=5)
        
        ttk.Label(api_frame, text="Compreface API Key:").grid(row=1, column=0, sticky="w", pady=5, padx=(0, 10))
        self.compreface_api_key_var = tk.StringVar()
        ttk.Entry(api_frame, textvariable=self.compreface_api_key_var).grid(row=1, column=1, sticky="ew", pady=5)
        
        # 学生管理系统API设置
        ttk.Separator(api_frame, orient="horizontal").grid(row=2, column=0, columnspan=3, sticky="ew", pady=10)
        
        ttk.Label(api_frame, text="学生管理系统 Base URL:").grid(row=3, column=0, sticky="w", pady=5, padx=(0, 10))
        self.student_system_base_url_var = tk.StringVar()
        ttk.Entry(api_frame, textvariable=self.student_system_base_url_var).grid(row=3, column=1, sticky="ew", pady=5)
        
        ttk.Label(api_frame, text="学生管理系统 Token:").grid(row=4, column=0, sticky="w", pady=5, padx=(0, 10))
        self.student_system_token_var = tk.StringVar()
        ttk.Entry(api_frame, textvariable=self.student_system_token_var).grid(row=4, column=1, sticky="ew", pady=5)
        
        # 保存按钮
        button_frame = ttk.Frame(frame)
        button_frame.grid(row=3, column=0, columnspan=3, pady=(0, 10))
        ttk.Button(button_frame, text="保存设置", command=self.save_settings, style="Accent.TButton").pack()
        
    def browse_image(self):
        filename = filedialog.askopenfilename(
            parent=self.root,
            title="选择图像文件",
            filetypes=[("图像文件", "*.jpg *.jpeg *.png *.bmp"), ("所有文件", "*.*")]
        )
        if filename:
            self.image_path_var.set(filename)
            
    def browse_modify_image(self, image_path_var):
        """为修改功能浏览图像"""
        filename = filedialog.askopenfilename(
            parent=self.root,
            title="选择图像文件",
            filetypes=[("图像文件", "*.jpg *.jpeg *.png *.bmp"), ("所有文件", "*.*")]
        )
        if filename:
            image_path_var.set(filename)
    
    def browse_multiple_modify_image(self, path_var, parent_window):
        """浏览批量修改图像文件"""
        filename = filedialog.askopenfilename(
            parent=parent_window,
            title="选择图像文件",
            filetypes=[("图像文件", "*.jpg *.jpeg *.png *.bmp"), ("所有文件", "*.*")]
        )
        if filename:
            path_var.set(filename)
            
    def browse_recognition_image(self):
        filename = filedialog.askopenfilename(
            parent=self.root,
            title="选择图像文件",
            filetypes=[("图像文件", "*.jpg *.jpeg *.png *.bmp"), ("所有文件", "*.*")]
        )
        if filename:
            self.recognition_image_path_var.set(filename)
            
    def browse_add_image(self):
        filename = filedialog.askopenfilename(
            parent=self.root,
            title="选择图像文件",
            filetypes=[("图像文件", "*.jpg *.jpeg *.png *.bmp"), ("所有文件", "*.*")]
        )
        if filename:
            self.add_image_path_var.set(filename)
            # 只有当用户没有输入姓名时，才将文件名基本名作为默认人员姓名
            if not self.person_name_var.get().strip():
                basename = os.path.splitext(os.path.basename(filename))[0]
                self.person_name_var.set(basename)
            
    def browse_database_file(self):
        filename = filedialog.asksaveasfilename(
            parent=self.root,
            title="选择数据库文件",
            filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")],
            defaultextension=".json"
        )
        if filename:
            self.database_file_var.set(filename)
            
    def detect_faces(self):
        image_path = self.image_path_var.get()
        if not image_path:
            messagebox.showerror("错误", "请选择图像文件", parent=self.root)
            return
            
        if not os.path.exists(image_path):
            messagebox.showerror("错误", "图像文件不存在", parent=self.root)
            return
            
        try:
            face_locations = detect_faces(image_path)
            self.detection_result.delete(1.0, tk.END)
            self.detection_result.insert(tk.END, f"检测到 {len(face_locations)} 个人脸:\n\n")
            for i, (top, right, bottom, left) in enumerate(face_locations):
                self.detection_result.insert(tk.END, f"人脸 {i+1}: 上={top}, 右={right}, 下={bottom}, 左={left}\n")
        except Exception as e:
            messagebox.showerror("错误", f"人脸检测失败: {str(e)}", parent=self.root)
            
    def recognize_faces(self):
        image_path = self.recognition_image_path_var.get()
        if not image_path:
            messagebox.showerror("错误", "请选择图像文件", parent=self.root)
            return
            
        if not os.path.exists(image_path):
            messagebox.showerror("错误", "图像文件不存在", parent=self.root)
            return
            
        try:
            face_locations = detect_faces(image_path)
            if not face_locations:
                messagebox.showinfo("结果", "未检测到人脸", parent=self.root)
                return
                
            face_encodings = extract_face_features(image_path)
            face_names = []
            for face_encoding in face_encodings:
                name, confidence = recognize_face(face_encoding, self.face_database)
                face_names.append(f"{name} ({confidence:.3f})")
            self.recognition_result.delete(1.0, tk.END)
            self.recognition_result.insert(tk.END, f"检测到 {len(face_locations)} 个人脸:\n\n")
            for i, (top, right, bottom, left) in enumerate(face_locations):
                self.recognition_result.insert(tk.END, f"人脸 {i+1}: 上={top}, 右={right}, 下={bottom}, 左={left}\n")
                self.recognition_result.insert(tk.END, f"  识别结果: {face_names[i]}\n")
        except Exception as e:
            messagebox.showerror("错误", f"人脸识别失败: {str(e)}", parent=self.root)
      
    def add_single_image(self, person_name, parent_window):
        """添加单张图片"""
        # 检查当前图片数量，限制每个人员最多10张图片
        from modules.face_database import get_person_face_count
        current_count = get_person_face_count(self.face_database, person_name)
        max_allowed = 10
        
        if current_count >= max_allowed:
            messagebox.showwarning("提示", f"每个人员最多只能有{max_allowed}张人脸图片，{person_name}已达到上限", parent=parent_window)
            return
        
        # 选择单个图像文件
        filename = filedialog.askopenfilename(
            parent=parent_window,
            title="选择图像文件",
            filetypes=[("图像文件", "*.jpg *.jpeg *.png *.bmp"), ("所有文件", "*.*")]
        )
        
        if not filename:
            return
        
        # 创建进度窗口
        progress_window = tk.Toplevel(parent_window)
        progress_window.title("添加图片进度")
        progress_window.geometry("300x150")
        progress_window.transient(parent_window)
        progress_window.grab_set()
        
        # 居中显示进度窗口
        progress_window.update_idletasks()
        x = parent_window.winfo_x() + (parent_window.winfo_width() // 2) - (300 // 2)
        y = parent_window.winfo_y() + (parent_window.winfo_height() // 2) - (150 // 2)
        progress_window.geometry(f"300x150+{x}+{y}")
        
        # 标题标签
        title_label = ttk.Label(progress_window, text="正在添加人脸照片...", font=("Arial", 12))
        title_label.pack(pady=10)
        
        # 进度条
        progress = ttk.Progressbar(progress_window, orient="horizontal", length=250, mode="indeterminate")
        progress.pack(pady=10)
        progress.start()
        
        # 状态标签
        status_label = ttk.Label(progress_window, text="正在处理图片...", font=("Arial", 10))
        status_label.pack(pady=5)
        
        # 更新进度窗口
        progress_window.update()
        
        try:
            # 确保目标人员文件夹存在且有写入权限
            person_dir = os.path.join("data", "face_images", person_name)
            try:
                os.makedirs(person_dir, exist_ok=True)
            except PermissionError:
                progress_window.destroy()
                messagebox.showerror("权限错误", f"没有权限创建文件夹: {person_dir}\n请以管理员身份运行程序或检查文件夹权限", parent=parent_window)
                return
            except Exception as e:
                progress_window.destroy()
                messagebox.showerror("错误", f"创建人员文件夹失败: {str(e)}\n请检查路径是否正确且有足够权限", parent=parent_window)
                return
        
            # 检查文件夹是否真的存在且可写
            if not os.path.exists(person_dir):
                progress_window.destroy()
                messagebox.showerror("错误", f"人员文件夹未创建: {person_dir}\n请检查磁盘空间和权限", parent=parent_window)
                return
                
            if not os.access(person_dir, os.W_OK):
                progress_window.destroy()
                messagebox.showerror("权限错误", f"没有写入权限到文件夹: {person_dir}\n请检查文件夹权限设置", parent=parent_window)
                return
            
            # 更新状态
            status_label.config(text="正在提取人脸特征...")
            progress_window.update()
            
            # 添加到人脸数据库（会自动复制文件到本地目录）
            add_result = add_face_to_database(person_name, filename, self.face_database)
            if add_result:
                # 更新状态
                status_label.config(text="正在保存数据库...")
                progress_window.update()
                
                # 保存数据库
                save_result = save_face_database(self.face_database, self.database_file)
                if save_result:
                    # 刷新人脸列表
                    self.refresh_face_list()
                    
                   
                    
                    # 刷新编辑窗口中的图片显示
                    self._refresh_edit_window_images(parent_window, person_name)
                    # 确保数据一致性
                    self.face_database = load_face_database(self.database_file)
                    
                    # 更新编辑窗口中的图片数量显示
                    self._update_image_count_display(parent_window, person_name)
                     # 关闭进度窗口
                    progress_window.destroy()
                    
                    messagebox.showinfo("成功", f"成功为 {person_name} 添加新的人脸图片", parent=parent_window)
                else:
                    progress_window.destroy()
                    messagebox.showerror("错误", "保存数据库失败", parent=parent_window)
            else:
                progress_window.destroy()
                messagebox.showerror("错误", "添加人脸图片失败：未检测到人脸或特征提取失败", parent=parent_window)
        except Exception as e:
            progress_window.destroy()
            messagebox.showerror("错误", f"添加人脸图片失败: {str(e)}", parent=parent_window)

    def add_multiple_images(self, person_name, parent_window):
        """批量添加图片"""
        # 检查当前图片数量，限制每个人员最多10张图片
        from modules.face_database import get_person_face_count
        current_count = get_person_face_count(self.face_database, person_name)
        max_allowed = 10
        
        if current_count >= max_allowed:
            messagebox.showwarning("提示", f"每个人员最多只能有{max_allowed}张人脸图片，{person_name}已达到上限", parent=parent_window)
            return
        
        # 选择多个图像文件
        filenames = filedialog.askopenfilenames(
            parent=parent_window,
            title="选择图像文件",
            filetypes=[("图像文件", "*.jpg *.jpeg *.png *.bmp"), ("所有文件", "*.*")]
        )
        
        if not filenames:
            return
        
        # 创建进度窗口
        progress_window = tk.Toplevel(parent_window)
        progress_window.title("批量添加进度")
        progress_window.geometry("400x300")
        progress_window.transient(parent_window)
        progress_window.grab_set()
        
        # 居中显示进度窗口
        progress_window.update_idletasks()
        x = parent_window.winfo_x() + (parent_window.winfo_width() // 2) - (400 // 2)
        y = parent_window.winfo_y() + (parent_window.winfo_height() // 2) - (300 // 2)
        progress_window.geometry(f"400x300+{x}+{y}")
        
        # 标题标签
        title_label = ttk.Label(progress_window, text="正在批量添加人脸照片...", font=("Arial", 12))
        title_label.pack(pady=10)
        
        # 当前处理文件标签
        current_file_label = ttk.Label(progress_window, text="准备开始导入...", font=("Arial", 10))
        current_file_label.pack(pady=5)
        
        # 进度条
        progress = ttk.Progressbar(progress_window, orient="horizontal", length=300, mode="determinate")
        progress.pack(pady=10)
        progress["value"] = 0
        progress["maximum"] = len(filenames)
        
        # 进度标签
        progress_label = ttk.Label(progress_window, text=f"0 / {len(filenames)}")
        progress_label.pack(pady=5)
        
        # 结果文本框
        result_frame = ttk.Frame(progress_window)
        result_frame.pack(fill="both", expand=True, padx=20, pady=10)
        result_frame.columnconfigure(0, weight=1)
        result_frame.rowconfigure(0, weight=1)
        
        result_text = tk.Text(result_frame, height=8, width=50)
        result_text.grid(row=0, column=0, sticky="nsew")
        
        scrollbar = ttk.Scrollbar(result_frame, orient="vertical", command=result_text.yview)
        scrollbar.grid(row=0, column=1, sticky="ns")
        result_text.configure(yscrollcommand=scrollbar.set)
        
        # 更新进度窗口
        progress_window.update()
        
        try:
            # 确保目标人员文件夹存在且有写入权限
            person_dir = os.path.join("data", "face_images", person_name)
            try:
                os.makedirs(person_dir, exist_ok=True)
            except PermissionError:
                messagebox.showerror("权限错误", f"没有权限创建文件夹: {person_dir}\n请以管理员身份运行程序或检查文件夹权限", parent=parent_window)
                progress_window.destroy()
                return
            except Exception as e:
                messagebox.showerror("错误", f"创建人员文件夹失败: {str(e)}\n请检查路径是否正确且有足够权限", parent=parent_window)
                progress_window.destroy()
                return
            
            # 检查文件夹是否真的存在且可写
            if not os.path.exists(person_dir):
                messagebox.showerror("错误", f"人员文件夹未创建: {person_dir}\n请检查磁盘空间和权限", parent=parent_window)
                progress_window.destroy()
                return
                
            if not os.access(person_dir, os.W_OK):
                messagebox.showerror("权限错误", f"没有写入权限到文件夹: {person_dir}\n请检查文件夹权限设置", parent=parent_window)
                progress_window.destroy()
                return
            
            success_count = 0
            fail_count = 0
            fail_list = []
            
            # 处理每个文件
            for i, filename in enumerate(filenames):
                # 更新当前处理文件标签
                current_file_label.config(text=f"正在处理: {os.path.basename(filename)}")
                progress_label.config(text=f"{i + 1} / {len(filenames)}")
                
                try:
                    # 使用文件名基本名作为人员姓名
                    result_text.insert(tk.END, f"正在导入 {os.path.basename(filename)} ...\n")
                    result_text.see(tk.END)
                    progress_window.update_idletasks()
                    
                    # 添加到人脸数据库（会自动复制文件到本地目录）
                    add_result = add_face_to_database(person_name, filename, self.face_database)
                    if add_result:
                        success_count += 1
                        result_text.insert(tk.END, f"  ✓ 成功\n")
                    else:
                        fail_count += 1
                        fail_list.append(filename)
                        result_text.insert(tk.END, f"  ✗ 失败\n")
                except Exception as e:
                    fail_count += 1
                    fail_list.append(filename)
                    result_text.insert(tk.END, f"  ✗ 错误: {str(e)}\n")
                
                # 更新进度条
                progress["value"] = i + 1
                progress_window.update_idletasks()
            
            # 保存数据库
            result_text.insert(tk.END, f"\n正在保存数据库...\n")
            progress_window.update_idletasks()
            
            if save_face_database(self.face_database, self.database_file):
                # 刷新人脸列表
                self.refresh_face_list()
                
                
                # 确保数据一致性
                self.face_database = load_face_database(self.database_file)
                
                result_text.insert(tk.END, f"数据库保存成功\n")
            else:
                result_text.insert(tk.END, f"数据库保存失败\n")
            
            # 显示最终结果
            result_text.insert(tk.END, f"\n导入完成: 成功 {success_count} 个, 失败 {fail_count} 个\n")
            if fail_list:
                result_text.insert(tk.END, f"失败列表:\n")
                for fail_file in fail_list:
                    result_text.insert(tk.END, f"  - {os.path.basename(fail_file)}\n")
            result_text.see(tk.END)
            progress_window.update_idletasks()
            
            # 等待一段时间让用户看到结果
            progress_window.after(2000, lambda: progress_window.destroy())  # 等待2秒后关闭窗口
            
            # 显示最终结果消息框
            if fail_count > 0:
                messagebox.showinfo("导入结果", f"成功导入 {success_count} 张人脸照片，失败 {fail_count} 张。", parent=parent_window)
            else:
                messagebox.showinfo("导入结果", f"成功导入 {success_count} 张人脸照片。", parent=parent_window)

            # 刷新编辑窗口中的图片显示
            self._refresh_edit_window_images(parent_window, person_name)
            # 更新编辑窗口中的图片数量显示
            self._update_image_count_display(parent_window, person_name)
            
        except Exception as e:
            progress_window.destroy()
            messagebox.showerror("错误", f"批量添加人脸图片失败: {str(e)}", parent=parent_window)
            return
    
    def add_face_to_db(self, image_path=None):
        """添加人脸到数据库"""
        name = self.person_name_var.get()
        image_path = image_path or self.add_image_path_var.get()
        
        if not name:
            messagebox.showerror("错误", "请输入人员姓名", parent=self.root)
            return
            
        if not image_path:
            messagebox.showerror("错误", "请选择图像文件", parent=self.root)
            return
            
        if not os.path.exists(image_path):
            messagebox.showerror("错误", "图像文件不存在", parent=self.root)
            return
            
        try:
            if add_face_to_database(name, image_path, self.face_database):
                if save_face_database(self.face_database, self.database_file):
                    messagebox.showinfo("成功", f"成功添加 {name} 到人脸数据库", parent=self.root)
                    self.person_name_var.set("")
                    self.add_image_path_var.set("")
                    self.refresh_face_list()  # 刷新人脸列表
                else:
                    messagebox.showerror("错误", "保存数据库失败", parent=self.root)
            else:
                messagebox.showerror("错误", "添加人脸失败", parent=self.root)
        except Exception as e:
            messagebox.showerror("错误", f"添加人脸失败: {str(e)}", parent=self.root)
         
    def load_api_config_to_ui(self):
        """将API配置加载到UI控件中"""
        # 加载Compreface配置
        compreface_config = self.api_config.get("compreface", {})
        self.compreface_base_url_var.set(compreface_config.get("base_url", ""))
        self.compreface_api_key_var.set(compreface_config.get("api_key", ""))
        
        # 加载学生管理系统配置
        student_config = self.api_config.get("student_management_system", {})
        self.student_system_base_url_var.set(student_config.get("base_url", ""))
        auth_config = student_config.get("auth", {})
        self.student_system_token_var.set(auth_config.get("token", ""))
    
    def save_settings(self):
        """保存设置"""
        # 保存数据库文件设置
        self.database_file = self.database_file_var.get()
        
        # 保存API配置
        # 更新Compreface配置
        if "compreface" not in self.api_config:
            self.api_config["compreface"] = {}
        self.api_config["compreface"]["base_url"] = self.compreface_base_url_var.get()
        self.api_config["compreface"]["api_key"] = self.compreface_api_key_var.get()
        
        # 更新学生管理系统配置
        if "student_management_system" not in self.api_config:
            self.api_config["student_management_system"] = {}
        self.api_config["student_management_system"]["base_url"] = self.student_system_base_url_var.get()
        if "auth" not in self.api_config["student_management_system"]:
            self.api_config["student_management_system"]["auth"] = {}
        self.api_config["student_management_system"]["auth"]["token"] = self.student_system_token_var.get()
        
        # 保存API配置到文件
        self.api_config_manager.update_config(self.api_config)
        
        messagebox.showinfo("成功", "设置已保存", parent=self.root)
        
    
    
    def refresh_face_list(self):
        """刷新人脸列表"""
        # 重新加载人脸数据库
        self.face_database = load_face_database(self.database_file)
        
        # 清空现有项目
        for item in self.face_tree.get_children():
            self.face_tree.delete(item)
        
        # 获取人脸列表并添加到Treeview
        try:
            from modules.face_database import get_person_face_count
            faces = list_faces_in_database(self.face_database)
            for name in sorted(faces):
                face_count = get_person_face_count(self.face_database, name)
                display_text = f"{name} ({face_count}张照片)"
                self.face_tree.insert("", "end", text=display_text)
            
            # 更新状态栏
            self.update_database_status()
        except Exception as e:
            messagebox.showerror("错误", f"刷新人脸列表失败: {str(e)}", parent=self.root)
    
    def update_database_status(self):
        """更新人脸库状态栏"""
        total_count = len(list_faces_in_database(self.face_database))
        selected_count = len(self.face_tree.selection())
        
        if selected_count > 0:
            self.database_status_var.set(f"总共 {total_count} 个人脸数据，已选中 {selected_count} 个")
        else:
            self.database_status_var.set(f"总共 {total_count} 个人脸数据")

    def preview_multiple_faces_internal(self):
        """内部方法：预览多个选中的人脸"""
        selected_items = self.face_tree.selection()
        if not selected_items:
            return
        
        # 获取所有选中的姓名
        selected_names = [self.face_tree.item(item)["text"] for item in selected_items]
        
        # 为每个人创建预览窗口
        for name in selected_names:
            # 检查是否已经打开了该人脸的预览窗口
            if name in self.preview_windows and self.preview_windows[name].winfo_exists():
                # 如果窗口已存在，将其置于前台
                self.preview_windows[name].lift()
                self.preview_windows[name].focus()
            else:
                # 创建新的预览窗口
                self.create_face_preview_window(name)
    
    def modify_selected_face(self):
        """修改选中的人脸（支持单个和多个）"""
        selected_items = self.face_tree.selection()
        if not selected_items:
            messagebox.showwarning("提示", "请先选择至多一个人脸", parent=self.root)
            return
        
        # 获取所有选中的姓名（从显示文本中提取纯姓名）
        selected_names = []
        for item in selected_items:
            display_text = self.face_tree.item(item)["text"]
            # 从"姓名 (N张照片)"格式中提取姓名
            if " (" in display_text:
                name = display_text.split(" (")[0]
            else:
                name = display_text
            selected_names.append(name)
        
        # 调用内部修改方法，传递正确的父窗口
        self._modify_faces_internal(selected_names)
    
    def _modify_faces_internal(self, selected_names):
        """内部修改人脸方法"""
        # 如果只选中一个，使用原来的编辑方式
        if len(selected_names) == 1:
            old_name = selected_names[0]
            
            # 获取旧图像路径
            old_image_paths = []
            face_infos = []
            if old_name in self.face_database:
                face_data = self.face_database[old_name]
                if isinstance(face_data, dict) and "encodings" in face_data:
                    # 新数据结构，获取所有图片的路径
                    for face_info in face_data["encodings"]:
                        image_path = face_info.get("image_path", "")
                        if image_path and os.path.exists(image_path):
                            old_image_paths.append(image_path)
                            face_infos.append(face_info)
                elif isinstance(face_data, dict) and "image_path" in face_data:
                    # 旧数据结构
                    image_path = face_data["image_path"]
                    if image_path and os.path.exists(image_path):
                        old_image_paths.append(image_path)
                        face_infos.append({"id": f"{old_name}_face_0", "image_path": image_path})
            
            # 创建修改窗口
            modify_window = tk.Toplevel(self.root)
            modify_window.title(f"编辑人脸信息 - {old_name}")
            modify_window.geometry("1000x750")  # 增加窗口宽度，使其更宽
            # 允许调整窗口大小
            modify_window.resizable(True, True)  # 允许调整窗口大小
            
            # 不再设置为模态窗口，允许用户自由操作
            # modify_window.transient(self.root)
            # modify_window.grab_set()
            
            # 确保窗口在最前端
            modify_window.lift()
            modify_window.focus_force()
            
            # 创建主框架
            main_frame = ttk.Frame(modify_window, padding="10")
            main_frame.pack(fill="both", expand=True)
            main_frame.columnconfigure(0, weight=1)
            main_frame.rowconfigure(1, weight=1)  # 让图像管理区域可以扩展
            
            # 姓名修改
            ttk.Label(main_frame, text="姓名:").grid(row=0, column=0, sticky="w", padx=5, pady=5)
            new_name_var = tk.StringVar(value=old_name)
            ttk.Entry(main_frame, textvariable=new_name_var, width=30).grid(row=0, column=1, padx=5, pady=5, sticky="w")
            
            # 图像管理区域
            image_management_frame = ttk.LabelFrame(main_frame, text="图像管理", padding="10")
            image_management_frame.grid(row=1, column=0, columnspan=2, sticky="nsew", pady=10)  # 添加nsew以允许垂直扩展
            image_management_frame.columnconfigure(0, weight=1)
            image_management_frame.rowconfigure(2, weight=1)  # 让预览区域可以垂直扩展
            
            # 显示当前图片数量
            from modules.face_database import get_person_face_count
            current_count = get_person_face_count(self.face_database, old_name)
            max_allowed = 10
            ttk.Label(image_management_frame, text=f"当前图片数量: {current_count}/{max_allowed} (最多{max_allowed}张)").grid(row=0, column=0, columnspan=5, sticky="w", pady=5)
            
            # 创建选中状态集合（存储在类属性中，使用窗口ID作为键）
            window_id = id(modify_window)
            if window_id not in self.selected_image_indices:
                self.selected_image_indices[window_id] = set()
            selected_image_indices = self.selected_image_indices[window_id]
            
            # 删除选中图片按钮功能
            def remove_selected_images():
                # 获取选中状态集合
                selected_image_indices = self.selected_image_indices.get(window_id, set())
                
                if not selected_image_indices:
                    messagebox.showwarning("提示", "请先选择要删除的图片", parent=modify_window)
                    return
                
                # 确认删除
                if messagebox.askyesno("确认删除", f"确定要删除选中的 {len(selected_image_indices)} 张图片吗？", parent=modify_window):
                    try:
                        # 导入删除图片的函数
                        from modules.face_database import remove_face_images
                        
                        # 转换为列表并排序（降序），以便正确删除
                        indices_to_remove = sorted(list(selected_image_indices), reverse=True)
                        
                        # 删除选中的图片
                        if remove_face_images(old_name, self.face_database, indices_to_remove):
                            # 保存数据库
                            if save_face_database(self.face_database, self.database_file):
                                # 清空选中状态
                                selected_image_indices.clear()
                                # 更新选中状态字典
                                self.selected_image_indices[window_id] = selected_image_indices
                                
                                # 不再关闭当前窗口，只刷新显示
                                self.refresh_face_list()
                                
                                # 重新加载人脸数据后再刷新编辑窗口中的图片显示
                                # 重新获取最新的人脸数据
                                self.face_database = load_face_database(self.database_file)
                                # 刷新编辑窗口中的图片显示
                                self._refresh_edit_window_images(modify_window, old_name)
                                # 确保数据一致性
                                self.face_database = load_face_database(self.database_file)
                                # 更新编辑窗口中的图片数量显示
                                self._update_image_count_display(modify_window, old_name)
                                # 设置消息框的父窗口为编辑窗口，确保不会被遮挡
                                messagebox.showinfo("成功", f"成功删除 {len(indices_to_remove)} 张图片", parent=modify_window)
                                
                            else:
                                messagebox.showerror("错误", "保存数据库失败", parent=modify_window)
                        else:
                            messagebox.showerror("错误", "删除图片失败", parent=modify_window)
                    except Exception as e:
                        messagebox.showerror("错误", f"删除图片失败: {str(e)}", parent=modify_window)
            
            # 保存按钮功能
            def save_changes():
                new_name = new_name_var.get().strip()
                
                if not new_name:
                    messagebox.showerror("错误", "请输入姓名", parent=modify_window)
                    return
                    
                try:
                    # 更新人脸数据
                    if old_name in self.face_database:
                        face_data = self.face_database[old_name]  # 确保 face_data 已定义
                        # 如果姓名改变了，需要更新键名和文件夹名
                        if new_name != old_name:
                            # 重命名人员文件夹
                            old_person_dir = os.path.join("data", "face_images", old_name)
                            new_person_dir = os.path.join("data", "face_images", new_name)
                            
                            # 如果新文件夹已存在，先删除
                            if os.path.exists(new_person_dir):
                                shutil.rmtree(new_person_dir)
                            
                            # 重命名文件夹（如果旧文件夹存在）
                            if os.path.exists(old_person_dir):
                                os.rename(old_person_dir, new_person_dir)
                            else:
                                # 如果旧文件夹不存在，创建新文件夹
                                os.makedirs(new_person_dir, exist_ok=True)
                            
                            # 更新数据库中所有图片的路径
                            if isinstance(face_data, dict) and "encodings" in face_data:
                                for face_info in face_data["encodings"]:
                                    old_image_path = face_info.get("image_path", "")
                                    if old_image_path:
                                        # 构造新的图片路径
                                        filename = os.path.basename(old_image_path)
                                        new_image_path = os.path.join(new_person_dir, filename)
                                        new_image_path = new_image_path.replace("\\", "/")  # 使用正斜杠路径格式
                                        face_info["image_path"] = new_image_path
                            
                            # 保存新的数据后删除旧的数据
                            if old_name in self.face_database:
                                del self.face_database[old_name]
                            
                            # 添加新条目（保持相同的图像数据）
                            self.face_database[new_name] = face_data
                            
                            # 保存数据库
                            if save_face_database(self.face_database, self.database_file):
                                messagebox.showinfo("成功", f"成功将 {old_name} 重命名为 {new_name}", parent=modify_window)
                                modify_window.destroy()
                                self.refresh_face_list()
                            else:
                                messagebox.showerror("错误", "保存数据库失败", parent=modify_window)
                        else:
                            messagebox.showinfo("成功", "没有更改", parent=modify_window)
                            modify_window.destroy()
                except Exception as e:
                    messagebox.showerror("错误", f"保存更改失败: {str(e)}", parent=modify_window)
            
            # 所有按钮放在同一行 - 添加图片按钮和操作按钮
            button_frame = ttk.Frame(image_management_frame)
            button_frame.grid(row=1, column=0, columnspan=5, pady=5, sticky="w")
            
            # 添加图片按钮
            ttk.Button(button_frame, text="添加图片", command=lambda: self.add_single_image(old_name, modify_window)).pack(side="left", padx=5)
            ttk.Button(button_frame, text="批量添加", command=lambda: self.add_multiple_images(old_name, modify_window)).pack(side="left", padx=5)
            ttk.Button(button_frame, text="摄像头添加", command=lambda: self.add_camera_image(old_name, modify_window)).pack(side="left", padx=5)
            
            # 操作按钮
            ttk.Button(button_frame, text="删除选中图片", command=remove_selected_images).pack(side="left", padx=5)
            save_button = ttk.Button(button_frame, text="保存", command=save_changes, style="Accent.TButton")
            save_button.pack(side="left", padx=5)
            ttk.Button(button_frame, text="取消", command=modify_window.destroy).pack(side="left", padx=5)
            
            # 现有图片预览区域
            # 即使没有图片也要显示预览区域，以便用户知道当前状态
            preview_frame = ttk.LabelFrame(image_management_frame, text="现有图片预览", padding="5")
            preview_frame.grid(row=2, column=0, columnspan=5, sticky="nsew", pady=(10, 0))
            preview_frame.columnconfigure(0, weight=1)
            preview_frame.rowconfigure(0, weight=1)
            
            # 增加预览区域的高度
            image_management_frame.rowconfigure(2, weight=1)  # 给预览区域更多权重
            
            # 双击查看大图功能
            def show_large_image(image_path):
                """显示大图窗口"""
                try:
                    # 创建大图窗口
                    large_window = tk.Toplevel(modify_window)
                    large_window.title("查看大图")
                    large_window.geometry("800x600")
                    large_window.resizable(True, True)
                    
                    # 确保窗口在最前端
                    large_window.lift()
                    large_window.focus_force()
                    
                    # 创建主框架
                    large_frame = ttk.Frame(large_window)
                    large_frame.pack(fill="both", expand=True, padx=10, pady=10)
                    large_frame.columnconfigure(0, weight=1)
                    large_frame.rowconfigure(0, weight=1)
                    
                    # 加载原始图像
                    from PIL import Image, ImageTk
                    original_image = Image.open(image_path)
                    
                    # 创建画布和滚动条来显示大图
                    canvas = tk.Canvas(large_frame, bg="black")
                    h_scrollbar = ttk.Scrollbar(large_frame, orient="horizontal", command=canvas.xview)
                    v_scrollbar = ttk.Scrollbar(large_frame, orient="vertical", command=canvas.yview)
                    canvas.configure(xscrollcommand=h_scrollbar.set, yscrollcommand=v_scrollbar.set)
                    
                    # 创建图像容器
                    image_container = ttk.Frame(canvas)
                    
                    # 调整图像大小以适应窗口
                    window_width = 780
                    window_height = 520
                    image_width, image_height = original_image.size
                    
                    # 计算缩放比例
                    scale_x = window_width / image_width
                    scale_y = window_height / image_height
                    scale = min(scale_x, scale_y, 1.0)  # 不放大图片
                    
                    if scale < 1.0:
                        new_width = int(image_width * scale)
                        new_height = int(image_height * scale)
                        resized_image = original_image.resize((new_width, new_height), Image.LANCZOS)
                    else:
                        resized_image = original_image
                        new_width, new_height = image_width, image_height
                    
                    # 转换为Tkinter兼容的格式
                    photo = ImageTk.PhotoImage(resized_image)
                    
                    # 创建标签显示图像
                    img_label = tk.Label(image_container, image=photo, bg="black")
                    img_label.__dict__['image'] = photo  # 保持引用以防止被垃圾回收
                    img_label.pack()
                    
                    # 将图像容器添加到画布
                    canvas.create_window((0, 0), window=image_container, anchor="nw")
                    # 修复：确保scrollregion参数有效
                    bbox = canvas.bbox("all")
                    if bbox:
                        # 修复：将bbox元组解包为独立参数
                        canvas.configure(scrollregion=(bbox[0], bbox[1], bbox[2], bbox[3]))
                    
                    # 布局
                    canvas.grid(row=0, column=0, sticky="nsew")
                    h_scrollbar.grid(row=1, column=0, sticky="ew")
                    v_scrollbar.grid(row=0, column=1, sticky="ns")
                    
                    # 添加下载按钮
                    def download_image():
                        """下载图片到本地"""
                        # 选择保存位置
                        save_path = filedialog.asksaveasfilename(
                            parent=large_window,
                            title="保存图片",
                            defaultextension=".jpg",
                            filetypes=[("JPEG files", "*.jpg"), ("PNG files", "*.png"), ("All files", "*.*")]
                        )
                        
                        if save_path:
                            try:
                                # 保存原始图像
                                original_image.save(save_path)
                                messagebox.showinfo("成功", f"图片已保存到:\n{save_path}", parent=large_window)
                            except Exception as e:
                                messagebox.showerror("错误", f"保存图片失败: {str(e)}", parent=large_window)
                    
                    # 下载按钮
                    download_button = ttk.Button(large_frame, text="下载图片", command=download_image)
                    download_button.grid(row=2, column=0, pady=10)
                    
                    # 添加鼠标滚轮支持
                    def _on_mousewheel(event):
                        canvas.yview_scroll(int(-1*(event.delta/120)), "units")
                    
                    def _on_shift_mousewheel(event):
                        canvas.xview_scroll(int(-1*(event.delta/120)), "units")
                    
                    canvas.bind("<MouseWheel>", _on_mousewheel)
                    canvas.bind("<Shift-MouseWheel>", _on_shift_mousewheel)
                    image_container.bind("<MouseWheel>", _on_mousewheel)
                    image_container.bind("<Shift-MouseWheel>", _on_shift_mousewheel)
                    img_label.bind("<MouseWheel>", _on_mousewheel)
                    img_label.bind("<Shift-MouseWheel>", _on_shift_mousewheel)
                    
                except Exception as e:
                    messagebox.showerror("错误", f"显示大图失败: {str(e)}", parent=modify_window)
            
            # 计算每行可以显示的图片数量
            def calculate_columns(frame_width, image_width=160):
                """根据框架宽度计算每行可以显示的图片数量"""
                # 考虑图片宽度、边距和间距
                total_image_width = image_width + 20  # 图片宽度 + 间距
                if total_image_width <= 0:
                    return 1
                cols = max(1, frame_width // total_image_width)
                return cols
            
            # 创建图片显示框架
            images_frame = tk.Frame(preview_frame, bg="white")
            images_frame.pack(fill="both", expand=True, padx=5, pady=5)
            
            # 创建Canvas和滚动条用于图片显示
            canvas = tk.Canvas(images_frame, bg="white")
            scrollbar = ttk.Scrollbar(images_frame, orient="vertical", command=canvas.yview)
            scrollable_frame = tk.Frame(canvas, bg="white")
            
            # 配置滚动
            scrollable_frame.bind(
                "<Configure>",
                lambda e: self._update_scrollregion(canvas)
            )
            
            canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
            canvas.configure(yscrollcommand=scrollbar.set)
            
            # 布局
            canvas.pack(side="left", fill="both", expand=True)
            scrollbar.pack(side="right", fill="y")
            
            # 添加鼠标滚轮支持
            def _on_mousewheel(event):
                canvas.yview_scroll(int(-1*(event.delta/120)), "units")
            
            canvas.bind("<MouseWheel>", _on_mousewheel)
            scrollable_frame.bind("<MouseWheel>", _on_mousewheel)
            
            # 显示图片的函数
            def display_images(image_data, sel_indices):
                """显示图片网格"""
                # 清除现有的图片
                for widget in scrollable_frame.winfo_children():
                    widget.destroy()
                
                if not image_data:
                    return
                
                # 获取框架宽度来计算每行显示的图片数量
                frame_width = canvas.winfo_width()
                if frame_width < 100:  # 初始时可能获取不到正确宽度
                    frame_width = 800  # 使用默认宽度
                
                cols = calculate_columns(frame_width)
                
                # 显示所有图片
                for i, (image_path, face_info) in enumerate(image_data):
                    row = i // cols
                    col = i % cols
                    
                    # 创建图片容器（无背景色）
                    img_container = tk.Frame(scrollable_frame, relief="raised", borderwidth=2, bg="white")
                    img_container.grid(row=row, column=col, padx=10, pady=10, sticky="n")
                    
                    try:
                        # 加载图像
                        from PIL import Image, ImageTk
                        image = Image.open(image_path)
                        
                        # 调整图像大小
                        image.thumbnail((150, 150), Image.LANCZOS)
                        
                        # 转换为Tkinter兼容的格式
                        photo = ImageTk.PhotoImage(image)
                        
                        # 创建标签显示图像
                        img_label = tk.Label(img_container, image=photo, bg="white")
                        img_label.__dict__['image'] = photo  # 保持引用以防止被垃圾回收
                        img_label.pack(pady=(5, 5))
                        
                        # 添加点击事件处理
                        def on_image_click(event, idx=i, container=img_container, win_id=window_id):
                            # 获取选中状态集合
                            sel_indices = self.selected_image_indices.get(win_id, set())
                            if idx in sel_indices:
                                sel_indices.remove(idx)
                                container.config(bg="white", relief="raised")
                            else:
                                sel_indices.add(idx)
                                container.config(bg="lightblue", relief="ridge")
                            # 更新选中状态
                            self.selected_image_indices[win_id] = sel_indices
                        
                        # 添加双击事件处理（查看大图）
                        def on_image_double_click(event, img_path=image_path):
                            show_large_image(img_path)
                        
                        img_label.bind("<Button-1>", on_image_click)
                        img_label.bind("<Double-Button-1>", on_image_double_click)
                                    
                        # 显示图片信息
                        basename = os.path.basename(image_path)
                        ttk.Label(img_container, text=f"{basename}", font=("Arial", 8), wraplength=150, background="white").pack()
                        ttk.Label(img_container, text=f"ID: {face_info.get('id', 'N/A')}", font=("Arial", 7), foreground="gray", background="white").pack()
                                    
                        # 如果图片已经被选中，设置为选中状态
                        if i in sel_indices:
                            img_container.config(bg="lightblue", relief="ridge")
                                    
                    except Exception as e:
                        ttk.Label(img_container, text=f"无法加载\n{os.path.basename(image_path)}", 
                                 foreground="red", font=("Arial", 8), background="white").pack(pady=20)
                            
                # 设置列的权重以确保正确对齐
                for j in range(cols):
                    scrollable_frame.columnconfigure(j, weight=1)
                        
            # 显示图片
            # 获取选中状态集合
            selected_image_indices = self.selected_image_indices.get(window_id, set())
            display_images(list(zip(old_image_paths, face_infos)), selected_image_indices)
                        
            # 添加窗口大小改变时的事件处理
            def on_canvas_configure(event):
                # 重新显示图片以适应新的宽度
                # 获取选中状态集合
                selected_image_indices = self.selected_image_indices.get(window_id, set())
                display_images(list(zip(old_image_paths, face_infos)), selected_image_indices)
                        
            # 绑定配置事件（窗口大小改变时触发）
            canvas.bind("<Configure>", on_canvas_configure)
        else:
            # 处理多个选中的人脸，提示只能选择一个人脸进行编辑
            messagebox.showwarning("提示", "只能选择一个人脸进行编辑",parent=self.root)
         
    
    def _refresh_edit_window_images(self, parent_window, person_name):
        """刷新编辑窗口中的图片显示"""
        # 不再重新加载人脸数据库，使用当前最新的数据
        # self.face_database = load_face_database(self.database_file)
        
        # 获取该人员的所有图片路径
        image_paths = []
        face_infos = []
        
        if person_name in self.face_database:
            face_data = self.face_database[person_name]
            if isinstance(face_data, dict) and "encodings" in face_data:
                # 新数据结构，获取所有图片的路径
                for face_info in face_data["encodings"]:
                    image_path = face_info.get("image_path", "")
                    if image_path and os.path.exists(image_path):
                        image_paths.append(image_path)
                        face_infos.append(face_info)
            elif isinstance(face_data, dict) and "image_path" in face_data:
                # 旧数据结构
                image_path = face_data["image_path"]
                if image_path and os.path.exists(image_path):
                    image_paths.append(image_path)
                    face_infos.append({"id": f"{person_name}_face_0", "image_path": image_path})
        
        # 查找预览区域（假设在parent_window中）
        preview_frame = None
        
        # 遍历所有子窗口查找预览区域
        def find_preview_frame(widget):
            nonlocal preview_frame
            if preview_frame:
                return  # 已找到，不需要继续查找
            
            if isinstance(widget, ttk.LabelFrame) and "图片预览" in widget.cget("text"):
                preview_frame = widget
                return
            
            # 递归查找子组件
            for child in widget.winfo_children():
                find_preview_frame(child)
        
        find_preview_frame(parent_window)
        
        # 如果找到了预览区域，更新其内容
        if preview_frame:
            # 清空现有内容
            try:
                children = list(preview_frame.winfo_children())
                for widget in children:
                    widget.destroy()
            except:
                pass  # 忽略可能的错误
            
            # 重新创建画布和滚动条来显示图片网格
            canvas = tk.Canvas(preview_frame, bg="white")
            scrollbar = ttk.Scrollbar(preview_frame, orient="vertical", command=canvas.yview)
            scrollable_frame = tk.Frame(canvas, bg="white")
            
            # 配置滚动区域
            def update_scroll_region(event=None):
                self._update_scrollregion(canvas)
            
            scrollable_frame.bind("<Configure>", update_scroll_region)

            canvas_window = canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
            canvas.configure(yscrollcommand=scrollbar.set)
            
            # 添加鼠标滚轮支持
            def _on_mousewheel(event):
                canvas.yview_scroll(int(-1*(event.delta/120)), "units")
            
            canvas.bind("<MouseWheel>", _on_mousewheel)
            scrollable_frame.bind("<MouseWheel>", _on_mousewheel)
            
            # 使用grid布局
            canvas.grid(row=0, column=0, sticky="nsew")
            scrollbar.grid(row=0, column=1, sticky="ns")
            preview_frame.columnconfigure(0, weight=1)
            preview_frame.rowconfigure(0, weight=1)

            if image_paths:
                # 计算每行可以显示的图片数量
                def calculate_columns(frame_width, image_width=160):
                    """根据框架宽度计算每行可以显示的图片数量"""
                    # 考虑图片宽度、边距和间距
                    total_image_width = image_width + 20  # 图片宽度 + 间距
                    if total_image_width <= 0:
                        return 1
                    cols = max(1, frame_width // total_image_width)
                    return cols
                
                # 显示所有图片
                def display_images():
                    # 获取框架宽度来计算每行显示的图片数量
                    frame_width = canvas.winfo_width()
                    if frame_width < 100:  # 初始时可能获取不到正确宽度
                        frame_width = 800  # 使用默认宽度
                    
                    cols = calculate_columns(frame_width)
                    
                    # 显示所有图片
                    for i, (image_path, face_info) in enumerate(zip(image_paths, face_infos)):
                        row = i // cols
                        col = i % cols
                        
                        # 创建图片容器（无背景色）
                        img_container = tk.Frame(scrollable_frame, relief="raised", borderwidth=2, bg="white")
                        img_container.grid(row=row, column=col, padx=10, pady=10, sticky="n")
                        
                        try:
                            # 加载图像
                            from PIL import Image, ImageTk
                            image = Image.open(image_path)
                            
                            # 调整图像大小
                            image.thumbnail((150, 150), Image.LANCZOS)
                            
                            # 转换为Tkinter兼容的格式
                            photo = ImageTk.PhotoImage(image)
                            
                            # 创建标签显示图像
                            img_label = tk.Label(img_container, image=photo, bg="white")
                            img_label.__dict__['image'] = photo  # 保持引用以防止被垃圾回收
                            img_label.pack(pady=(5, 5))
                            
                            # 添加点击事件处理
                            def make_click_handler(index, container):
                                def on_image_click(event):
                                    # 获取窗口ID
                                    win_id = id(parent_window)
                                    # 获取选中状态集合
                                    sel_indices = self.selected_image_indices.get(win_id, set())
                                    if index in sel_indices:
                                        sel_indices.remove(index)
                                        container.config(bg="white", relief="raised")
                                    else:
                                        sel_indices.add(index)
                                        container.config(bg="lightblue", relief="ridge")
                                    # 更新选中状态
                                    self.selected_image_indices[win_id] = sel_indices
                                return on_image_click
                            
                            img_label.bind("<Button-1>", make_click_handler(i, img_container))
                            
                            # 显示图片信息
                            basename = os.path.basename(image_path)
                            ttk.Label(img_container, text=f"{basename}", font=("Arial", 8), wraplength=150, background="white").pack()
                            ttk.Label(img_container, text=f"ID: {face_info.get('id', 'N/A')}", font=("Arial", 7), foreground="gray", background="white").pack()
                            
                        except Exception as e:
                            ttk.Label(img_container, text=f"无法加载\n{os.path.basename(image_path)}", 
                                     foreground="red", font=("Arial", 8), background="white").pack(pady=20)
                    
                    # 设置列的权重以确保正确对齐
                    for j in range(cols):
                        scrollable_frame.columnconfigure(j, weight=1)
                
                # 显示图片
                display_images()
                
                # 添加窗口大小改变时的事件处理
                def on_canvas_configure(event):
                    # 重新显示图片以适应新的宽度
                    # 先清除现有图片
                    for widget in scrollable_frame.winfo_children():
                        widget.destroy()
                    # 重新显示图片
                    display_images()
                
                # 绑定配置事件（窗口大小改变时触发）
                canvas.bind("<Configure>", on_canvas_configure)
            else:
                # 没有图片时显示提示信息
                no_image_label = ttk.Label(scrollable_frame, text="当前没有图片，请添加图片", font=("Arial", 10), foreground="gray")
                no_image_label.grid(row=0, column=0, pady=20)
                
                # 添加鼠标滚轮支持
                def _on_mousewheel(event):
                    pass  # 没有内容可滚动
                
                scrollable_frame.bind("<MouseWheel>", _on_mousewheel)
          
    

    def _update_image_count_display(self, parent_window, person_name):
        """更新编辑窗口中的图片数量显示"""
        try:
            # 查找图片数量标签并更新
            from modules.face_database import get_person_face_count
            current_count = get_person_face_count(self.face_database, person_name)
            max_allowed = 10
            
            # 首先尝试通过窗口属性查找图片数量标签
            if hasattr(self, '_window_data'):
                window_id = id(parent_window)
                if window_id in self._window_data:
                    window_data = self._window_data[window_id]
                    if window_data.get('person_name') == person_name:
                        window_data['image_count_label'].config(text=f"当前图片数量: {current_count}/{max_allowed} (最多{max_allowed}张)")
                        return True
            
            # 遍历所有子组件查找图片数量标签
            def find_count_label(widget):
                if isinstance(widget, ttk.Label) and "当前图片数量:" in widget.cget("text"):
                    widget.config(text=f"当前图片数量: {current_count}/{max_allowed} (最多{max_allowed}张)")
                    return True
                
                # 递归查找子组件
                try:
                    children = list(widget.winfo_children())
                    for child in children:
                        if find_count_label(child):
                            return True
                except:
                    pass  # 忽略可能的错误
                return False
            
            # 如果在当前窗口中找不到标签，尝试在图像管理框架中查找
            image_management_frame = None
            
            def find_image_management_frame(widget):
                nonlocal image_management_frame
                if isinstance(widget, ttk.LabelFrame) and widget.cget("text") == "图像管理":
                    image_management_frame = widget
                    return True
                
                # 递归查找子组件
                try:
                    children = list(widget.winfo_children())
                    for child in children:
                        if find_image_management_frame(child):
                            return True
                except:
                    pass  # 忽略可能的错误
                return False
            
            # 首先尝试查找图像管理框架
            find_image_management_frame(parent_window)
            
            # 如果找到了图像管理框架，直接在其中查找并更新标签
            if image_management_frame is not None:
                try:
                    children = list(image_management_frame.winfo_children())
                    for child in children:
                        if isinstance(child, ttk.Label) and "当前图片数量:" in child.cget("text"):
                            child.config(text=f"当前图片数量: {current_count}/{max_allowed} (最多{max_allowed}张)")
                            return True
                except:
                    pass  # 忽略可能的错误
            
            # 如果没有找到图像管理框架，使用原来的查找方法
            find_count_label(parent_window)
        except Exception as e:
            print(f"更新图片数量显示失败: {e}")
    
    def add_camera_image(self, person_name, parent_window):
        """使用摄像头添加图片"""
        # 检查当前图片数量，限制每个人员最多10张图片
        from modules.face_database import get_person_face_count
        current_count = get_person_face_count(self.face_database, person_name)
        max_allowed = 10
        
        if current_count >= max_allowed:
            messagebox.showwarning("提示", f"每个人员最多只能有{max_allowed}张人脸图片，{person_name}已达到上限", parent=parent_window)
            return
            
        # 检查可用的摄像头
        available_cameras = self._get_available_cameras()
        
        # 如果有多个摄像头，让用户选择
        camera_index = 0
        if len(available_cameras) > 1:
            camera_index = self._select_camera(available_cameras, parent_window)
            if camera_index is None:
                return  # 用户取消了选择
        elif len(available_cameras) == 0:
            messagebox.showerror("摄像头错误", "未找到可用的摄像头", parent=parent_window)
            return
            
        # 检查并确保必要的目录存在且有写入权限
        if not self._check_and_create_directories():
            messagebox.showerror("权限错误", "无法创建或访问必要的目录，请以管理员身份运行程序或检查文件夹权限", parent=parent_window)
            return
            
        try:
            # 检查OpenCV是否可用
            if cv2 is None:
                raise ImportError("OpenCV库未安装，请确保已安装opencv-python库")
            
            # 初始化摄像头
            video_capture = getattr(cv2, 'VideoCapture')(camera_index)
            
            if not video_capture.isOpened():
                raise Exception(f"无法打开摄像头 {camera_index}")
            
            # 创建摄像头窗口
            camera_window = tk.Toplevel(parent_window)
            camera_window.title("摄像头拍照")
            camera_window.geometry("640x480")
            
            # 设置窗口为模态窗口，保持在前端
            camera_window.transient(parent_window)
            camera_window.grab_set()
            # 创建显示区域
            video_label = ttk.Label(camera_window)
            video_label.pack(pady=10)
            
            # 状态标签
            status_label = ttk.Label(camera_window, text="正在启动摄像头...")
            status_label.pack()
            
            # 按钮框架
            button_frame = ttk.Frame(camera_window)
            button_frame.pack(pady=10)
            
            # 控制变量
            capturing = True
            
            def capture_image():
                nonlocal capturing
                # capturing = False  # 不再关闭摄像头，允许继续拍照
                
                # 获取当前帧
                ret, frame = video_capture.read()
                if ret:
                    try:
                        # 生成文件名（处理中文人名）
                        timestamp = int(time.time())
                        # 确保文件名中的中文字符被正确处理
                        safe_person_name = person_name.replace("/", "_").replace("\\", "_").replace(":", "_").replace("*", "_").replace("?", "_").replace("\"", "_").replace("<", "_").replace(">", "_").replace("|", "_")
                        filename = f"{safe_person_name}_{timestamp}.jpg"
                        
                        # 确保人员文件夹存在（处理中文人名）
                        person_dir = os.path.join("data", "face_images", person_name)
                        # 确保使用正确的编码处理中文路径
                        person_dir = os.path.normpath(person_dir)
                        # 在Windows系统上，确保路径编码正确
                        if os.name == 'nt':
                            person_dir = person_dir.encode('utf-8').decode('utf-8')
                      
                        try:
                            os.makedirs(person_dir, exist_ok=True)
                        except PermissionError:
                            # 尝试修复权限
                            if not self._fix_directory_permissions(person_dir):
                                messagebox.showerror("权限错误", f"没有权限创建文件夹: {person_dir}\n请以管理员身份运行程序或检查文件夹权限", parent=parent_window)
                                return
                        except Exception as e:
                            messagebox.showerror("错误", f"创建人员文件夹失败: {str(e)}\n请检查路径是否正确且有足够权限", parent=parent_window)
                            return
                        
                        # 检查文件夹是否真的存在且可写
                        if not os.path.exists(person_dir):
                            # 尝试修复权限
                            if not self._fix_directory_permissions(person_dir):
                                messagebox.showerror("错误", f"人员文件夹未创建: {person_dir}\n请检查磁盘空间和权限", parent=parent_window)
                                return
                            
                        if not os.access(person_dir, os.W_OK):
                            # 尝试修复权限
                            if not self._fix_directory_permissions(person_dir):
                                messagebox.showerror("权限错误", f"没有写入权限到文件夹: {person_dir}\n请检查文件夹权限设置", parent=parent_window)
                                return
                        
                        # 构造完整文件路径
                        filepath = os.path.join(person_dir, filename)
                        # 确保使用正确的路径分隔符
                        filepath = os.path.normpath(filepath)
                       
                        # 在Windows系统上，确保路径编码正确
                        if os.name == 'nt':
                            filepath = filepath.encode('utf-8').decode('utf-8')
                            
                        # 保存图像
                        # 尝试保存图像，添加更完善的错误处理
                        filepath_for_save = filepath
                       
                        # 使用Unicode编码处理文件路径，确保中文字符正确处理
                        if os.name == 'nt':
                            filepath_for_save = filepath.encode('utf-8').decode('utf-8')
                          
                        # 尝试保存图像，添加更完善的错误处理
                        try:
                            try:
                                if cv2 is not None:
                                    # 安全使用cv2相关功能
                                    ret2, buf = getattr(cv2, 'imencode')('.jpg', frame)
                                else:
                                    # 处理无cv2的情况（如报错提示、使用替代方案等）
                                    raise ImportError("需要安装OpenCV: pip install opencv-python")
                                if ret2:
                                    # 用 Python 内置 open 写入（支持中文路径）
                                    file_temp_path=os.path.join("data", "temp", filename)
                                    with open(filepath_for_save, 'wb') as f:
                                        f.write(buf)
                                    success = True
                                else:
                                    success = False
                                # success = getattr(cv2, 'imwrite')(filepath_for_save, frame)
                              
                                if not success:
                                    messagebox.showerror("错误", f"保存图片失败: 无法写入文件 {filepath}\n请检查文件夹权限和磁盘空间", parent=parent_window)
                                    return
                            except Exception as save_error:
                                messagebox.showerror("错误", f"保存图片失败: {str(save_error)}\n文件路径: {filepath}\n请检查文件夹权限和磁盘空间", parent=parent_window)
                                return
                        except PermissionError:
                           
                            # 尝试修复权限
                            if not self._fix_directory_permissions(person_dir):
                                messagebox.showerror("权限错误", f"没有权限写入文件: {filepath}\n请以管理员身份运行程序或检查文件夹权限", parent=parent_window)
                                return
                            else:
                                # 权限修复成功后重试保存
                                try:
                                   
                                    if cv2 is not None:
                                        # 安全使用cv2相关功能
                                        ret2, buf = getattr(cv2, 'imencode')('.jpg', frame)
                                    else:
                                        # 处理无cv2的情况（如报错提示、使用替代方案等）
                                        raise ImportError("需要安装OpenCV: pip install opencv-python")
                                    if ret2:
                                        # 用 Python 内置 open 写入（支持中文路径）
                                        with open(filepath_for_save, 'wb') as f:
                                            f.write(buf)
                                        success = True
                                    else:
                                        success = False
                                    if not success:
                                        messagebox.showerror("错误", f"保存图片失败: 无法写入文件 {filepath}\n请检查文件夹权限和磁盘空间", parent=parent_window)
                                        return
                                except Exception as e:
                                    messagebox.showerror("错误", f"保存图片时发生异常: {str(e)}", parent=parent_window)
                                    return
                        except Exception as e:
                            messagebox.showerror("错误", f"保存图片时发生异常: {str(e)}", parent=parent_window)
                            return
                        
                        # 检查文件是否成功保存
                        filepath_for_check = filepath
                        if os.name == 'nt':
                            filepath_for_check = filepath.encode('utf-8').decode('utf-8')
                            
                        if not os.path.exists(filepath_for_check):
                            messagebox.showerror("错误", f"保存图片失败: 文件未创建 {filepath}\n请检查磁盘空间和权限", parent=parent_window)
                            return
                        
                        # 检查文件大小
                        try:
                            file_size = os.path.getsize(filepath_for_check)
                            if file_size == 0:
                                messagebox.showerror("错误", f"保存图片失败: 文件大小为0 {filepath}", parent=parent_window)
                                return
                        except Exception as e:
                            messagebox.showerror("错误", f"检查文件大小时出错: {str(e)}", parent=parent_window)
                            return
                       
                        # 添加到人脸数据库
                        try:
                            add_result = add_face_to_database(person_name, filepath, self.face_database)
                            if add_result:
                                save_result = save_face_database(self.face_database, self.database_file)
                                if save_result:
                                    # 刷新人脸列表
                                    self.refresh_face_list()
                                    
                                    # 检查添加后是否达到上限
                                    new_count = get_person_face_count(self.face_database, person_name)
                                    if new_count >= max_allowed:
                                        # 达到上限，关闭摄像头窗口
                                        capturing = False
                                        camera_window.destroy()
                                        video_capture.release()
                                        messagebox.showinfo("成功", f"成功为 {person_name} 添加新的人脸图片\n已达到照片数量上限({max_allowed}张)", parent=parent_window)
                                    else:
                                        # 未达到上限，继续拍照
                                        messagebox.showinfo("成功", f"成功为 {person_name} 添加新的人脸图片\n还可以添加 {max_allowed - new_count} 张照片", parent=parent_window)
                                        # 更新状态标签显示当前照片数量
                                        status_label.config(text=f"点击'拍照'按钮拍摄照片\n当前已有{new_count + 1}张，最多{max_allowed}张")
                                        
                                    # 刷新编辑窗口中的图片显示
                                    self._refresh_edit_window_images(parent_window, person_name)
                                    # 确保数据一致性
                                    self.face_database = load_face_database(self.database_file)
                                    
                                    # 更新编辑窗口中的图片数量显示
                                    self._update_image_count_display(parent_window, person_name)
                                else:
                                    messagebox.showerror("错误", "保存数据库失败", parent=parent_window)
                            else:
                                messagebox.showerror("错误", "添加人脸图片失败：未检测到人脸或特征提取失败", parent=parent_window)
                        except Exception as e:
                            messagebox.showerror("错误", f"添加人脸图片失败: {str(e)}", parent=parent_window)
                            
                    except Exception as e:
                        # 设置消息框的父窗口为编辑窗口，确保不会被遮挡
                        messagebox.showerror("错误", f"保存图片失败: {str(e)}", parent=parent_window)
                else:
                    messagebox.showerror("错误", "无法获取摄像头图像", parent=parent_window)
            
            def cancel_capture():
                nonlocal capturing
                capturing = False
                camera_window.destroy()
                video_capture.release()
            
            # 创建按钮
            ttk.Button(button_frame, text="拍照", command=capture_image, style="Accent.TButton").pack(side="left", padx=5)
            ttk.Button(button_frame, text="取消", command=cancel_capture).pack(side="left", padx=5)
            
            def update_video():
                nonlocal capturing
                if capturing:
                    ret, frame = video_capture.read()
                    if ret:
                        # 转换颜色空间
                        rgb_frame = getattr(cv2, 'cvtColor')(frame, getattr(cv2, 'COLOR_BGR2RGB'))
                        
                        # 调整大小
                        rgb_frame = getattr(cv2, 'resize')(rgb_frame, (480, 360))
                        
                        # 转换为PIL图像
                        from PIL import Image, ImageTk
                        pil_image = Image.fromarray(rgb_frame)
                        photo = ImageTk.PhotoImage(image=pil_image)
                        
                        # 更新显示
                        video_label.configure(image=photo)
                        video_label.__dict__['image'] = photo  # 保持引用
                        
                        # 检查当前照片数量并更新状态
                        current_count = get_person_face_count(self.face_database, person_name)
                        status_label.config(text=f"点击'拍照'按钮拍摄照片\n当前已有{current_count}张，最多{max_allowed}张")
                        
                        # 继续更新
                        camera_window.after(30, update_video)
                    else:
                        status_label.config(text="无法获取摄像头图像")
                else:
                    try:
                        video_capture.release()
                    except:
                        pass
            
            # 确保在窗口关闭时释放摄像头
            def on_closing():
                nonlocal capturing
                capturing = False
                try:
                    video_capture.release()
                except:
                    pass
                camera_window.destroy()
            
            camera_window.protocol("WM_DELETE_WINDOW", on_closing)
            
            # 开始更新视频
            update_video()
            
        except ImportError as e:
            messagebox.showerror("摄像头错误", f"缺少必要的库: {str(e)}\n请确保已安装opencv-python和Pillow库", parent=parent_window)
        except Exception as e:
            messagebox.showerror("摄像头错误", f"摄像头功能出错: {str(e)}", parent=parent_window)
    
    def _check_and_create_directories(self):
        """检查并创建必要的目录"""
        try:
            # 确保data目录存在
            data_dir = "data"
            if not os.path.exists(data_dir):
                os.makedirs(data_dir, mode=0o755)
            
            # 确保face_images目录存在
            face_images_dir = os.path.join(data_dir, "face_images")
            if not os.path.exists(face_images_dir):
                os.makedirs(face_images_dir, mode=0o755)
                
            # 尝试修复权限
            self._fix_directory_permissions(data_dir)
            self._fix_directory_permissions(face_images_dir)
                
            # 检查写入权限
            if not os.access(data_dir, os.W_OK) or not os.access(face_images_dir, os.W_OK):
                return False
                
            return True
        except PermissionError:
            return False
        except Exception:
            return False
    
    def _fix_directory_permissions(self, directory):
        """尝试修复目录权限问题"""
        try:
            # 确保父目录存在
            parent_dir = os.path.dirname(directory)
            if parent_dir and not os.path.exists(parent_dir):
                os.makedirs(parent_dir, exist_ok=True)
            
            # 检查目录是否存在
            if not os.path.exists(directory):
                # 尝试创建目录，使用更宽松的权限
                os.makedirs(directory, mode=0o755, exist_ok=True)
            
            # 检查是否可写
            if not os.access(directory, os.W_OK):
                # 在Windows上尝试修改权限
                if os.name == 'nt':
                    import stat
                    # 使用更宽松的权限设置
                    os.chmod(directory, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC)
                    # 尝试修改父目录权限
                    if parent_dir and os.path.exists(parent_dir):
                        os.chmod(parent_dir, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC)
                else:
                    # 在Unix/Linux系统上设置权限
                    os.chmod(directory, 0o755)
                    # 尝试修改父目录权限
                    if parent_dir and os.path.exists(parent_dir):
                        os.chmod(parent_dir, 0o755)
                    
            # 再次检查权限
            return os.access(directory, os.W_OK)
        except Exception as e:
            print(f"修复目录权限时出错: {e}")
            return False
    
    def delete_selected_face(self):
        """删除选中的人脸"""
        selected_items = self.face_tree.selection()
        if not selected_items:
            messagebox.showwarning("提示", "请先选择要删除的人脸", parent=self.root)
            return
            
        # 获取所有选中的姓名（从显示文本中提取纯姓名）
        selected_names = []
        for item in selected_items:
            display_text = self.face_tree.item(item)["text"]
            # 从"姓名 (N张照片)"格式中提取姓名
            if " (" in display_text:
                name = display_text.split(" (")[0]
            else:
                name = display_text
            selected_names.append(name)
        
        # 确认删除
        if len(selected_names) == 1:
            confirm_message = f"确定要删除 {selected_names[0]} 的人脸数据吗？"
        else:
            confirm_message = f"确定要删除以下 {len(selected_names)} 个人的人脸数据吗？\n" + \
                            "\n".join([f"- {name}" for name in selected_names[:10]]) + \
                            ("\n..." if len(selected_names) > 10 else "")
        
        if messagebox.askyesno("确认删除", confirm_message, parent=self.root):
            try:
                success_count = 0
                fail_count = 0
                fail_list = []
                
                # 删除每个人脸数据
                for name in selected_names:
                    if remove_face_from_database(name, self.face_database):
                        success_count += 1
                    else:
                        fail_count += 1
                        fail_list.append(name)
                
                # 保存数据库
                if save_face_database(self.face_database, self.database_file):
                    self.refresh_face_list()
                    
                    # 显示结果
                    if fail_count == 0:
                        messagebox.showinfo("成功", f"成功删除 {success_count} 个人的人脸数据", parent=self.root)
                    else:
                        fail_message = f"成功删除 {success_count} 个人的人脸数据，\n{fail_count} 个删除失败"
                        if fail_list:
                            fail_message += f"\n删除失败的人员: {', '.join(fail_list)}"
                        messagebox.showwarning("部分成功", fail_message, parent=self.root)
                else:
                    messagebox.showerror("错误", "保存数据库失败", parent=self.root)
            except Exception as e:
                messagebox.showerror("错误", f"删除人脸数据失败: {str(e)}", parent=self.root)
    
    def on_face_tree_double_click(self, event):
        """人脸库条目双击事件处理"""
        selected_items = self.face_tree.selection()
        if selected_items:
            # 双击直接打开编辑窗口，而不是预览窗口
            self.modify_selected_face()
    
    def preview_multiple_faces(self):
        """预览多个选中的人脸"""
        selected_items = self.face_tree.selection()
        if not selected_items:
            messagebox.showwarning("提示", "请先选择至少一个人脸", parent=self.root)
            return
        
        # 获取所有选中的姓名
        selected_names = [self.face_tree.item(item)["text"] for item in selected_items]
        
        # 为每个人创建预览窗口
        for name in selected_names:
            # 检查是否已经打开了该人脸的预览窗口
            if name in self.preview_windows and self.preview_windows[name].winfo_exists():
                # 如果窗口已存在，将其置于前台
                self.preview_windows[name].lift()
                self.preview_windows[name].focus()
            else:
                # 创建新的预览窗口
                self.create_face_preview_window(name)
    
    def create_face_preview_window(self, name):
        """创建单个人脸预览窗口"""
        # 获取图像路径
        image_paths = []
        face_infos = []  # 存储人脸信息用于显示
        if name in self.face_database:
            face_data = self.face_database[name]
            if isinstance(face_data, dict) and "encodings" in face_data:
                # 新数据结构，可能有多张图片
                for face_info in face_data["encodings"]:
                    image_path = face_info.get("image_path", "")
                    # 处理路径分隔符，确保跨平台兼容性
                    if image_path:
                        image_path = image_path.replace("/", os.sep).replace("\\", os.sep)
                    if image_path and os.path.exists(image_path):
                        image_paths.append(image_path)
                        face_infos.append(face_info)
            elif isinstance(face_data, dict) and "image_path" in face_data:
                image_path = face_data["image_path"]
                if image_path:
                    image_path = image_path.replace("/", os.sep).replace("\\", os.sep)
                if image_path and os.path.exists(image_path):
                    image_paths.append(image_path)
                    face_infos.append(face_data)
        
        if not image_paths:
            messagebox.showwarning("提示", f"未找到 {name} 的图像", parent=self.root)
            return
        
        # 创建预览窗口
        preview_window = tk.Toplevel(self.root)
        preview_window.title(f"预览 - {name}")
        preview_window.geometry("400x400")
        preview_window.protocol("WM_DELETE_WINDOW", lambda: self.close_preview_window(name))
        self.preview_windows[name] = preview_window
        
        # 创建滚动视图
        scrollable_frame = tk.Frame(preview_window)
        scrollable_frame.pack(fill=tk.BOTH, expand=True)
        # 使用Canvas来实现滚动功能
        canvas = tk.Canvas(scrollable_frame)
        scrollbar = tk.Scrollbar(preview_window, orient=tk.VERTICAL, command=canvas.yview)
        canvas.configure(yscrollcommand=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # 创建画布
        canvas = tk.Canvas(scrollable_frame)
        canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        canvas.bind("<Configure>", lambda e: self._update_scrollregion(canvas))
        
        # 创建内部框架
        inner_frame = tk.Frame(canvas)
        canvas.create_window((0, 0), window=inner_frame, anchor="nw")
        
        # 显示图像
        try:
            from PIL import Image, ImageTk
            for image_path, face_info in zip(image_paths, face_infos):
                image = Image.open(image_path)
                image = image.resize((300, 300), Image.LANCZOS)  # 使用LANCZOS替代ANTIALIAS
                photo = ImageTk.PhotoImage(image)
                label = tk.Label(inner_frame, image=photo)
                label.__dict__['image'] = photo  # 保持对图像的引用
                label.pack(pady=10)
                
                # 显示人脸信息
                info_label = tk.Label(inner_frame, text=str(face_info))
                info_label.pack(pady=5)
        except Exception as e:
            # 如果PIL不可用，显示错误信息
            error_label = tk.Label(inner_frame, text=f"无法加载图像: {str(e)}", fg="red")
            error_label.pack(pady=10)
    
    def close_preview_window(self, name):
        """关闭预览窗口"""
        if name in self.preview_windows:
            self.preview_windows[name].destroy()
            del self.preview_windows[name]
    
   
   
    def _display_multiple_images_with_grid(self, parent_frame, image_paths, name, face_infos):
        """使用网格布局显示多张图片"""
        from PIL import Image, ImageTk
        
        # 获取父窗口引用
        parent_window = parent_frame.winfo_toplevel()
        person_name = name
        
        # 清除之前的显示
        for widget in parent_frame.winfo_children():
            if isinstance(widget, ttk.Frame) and widget.grid_info().get("row", 0) == 1:
                widget.destroy()
        
        # 创建图片网格显示区域
        grid_frame = ttk.Frame(parent_frame)
        grid_frame.grid(row=1, column=0, sticky="nsew", pady=(0, 10))
        grid_frame.columnconfigure(0, weight=1)
        parent_frame.rowconfigure(1, weight=1)
        
        # 创建画布和滚动条来显示图片网格
        canvas = tk.Canvas(grid_frame)
        scrollbar = ttk.Scrollbar(grid_frame, orient="vertical", command=canvas.yview)
        scrollable_frame = ttk.Frame(canvas)
        
        scrollable_frame.bind(
            "<Configure>",
            lambda e: self._update_scrollregion(canvas)
        )
        
        canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)
        
        # 使用grid布局
        canvas.grid(row=0, column=0, sticky="nsew")
        scrollbar.grid(row=0, column=1, sticky="ns")

        # 创建选中状态集合（存储在类属性中，使用窗口ID作为键）
        window_id = id(parent_window)
        if window_id not in self.selected_image_indices:
            self.selected_image_indices[window_id] = set()
        selected_image_indices = self.selected_image_indices[window_id]
        
        # 显示所有图片
        # 固定每排显示5个图片
        cols = 5  # 固定每排5个图片
                
        # 空的calculate_columns函数（保持代码结构）
        def calculate_columns(canvas_width, image_width=160, padding=10):
            """固定返回5列"""
            return 5
                
        # 当窗口大小改变时保持5列
        def on_canvas_configure(event):
            nonlocal cols
            cols = 5  # 始终保持5列
            # 重新排列图片
            rearrange_images()
                
        canvas.bind("<Configure>", on_canvas_configure)
        
        # 重新排列图片的函数
        def rearrange_images():
            nonlocal cols
            # 重新计算列数
            canvas.update_idletasks()
            canvas_width = canvas.winfo_width()
            if canvas_width > 10:
                cols = calculate_columns(canvas_width)
                    
            # 重新排列所有图片
            children = scrollable_frame.winfo_children()
            for j, widget in enumerate(children):
                if isinstance(widget, tk.Frame):
                    row = j // cols
                    col = j % cols
                    widget.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
            # 更新列的权重
            for j in range(cols):
                scrollable_frame.columnconfigure(j, weight=1)
                
        # 显示所有图片（每排5个）
        cols = 5  # 固定每排5个图片
        for i, (image_path, face_info) in enumerate(zip(image_paths, face_infos)):
            row = i // cols
            col = i % cols
            
            # 创建图片容器
            img_container = tk.Frame(scrollable_frame, relief="raised", borderwidth=2)
            img_container.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
            scrollable_frame.columnconfigure(col, weight=1)
                    
            try:
                # 加载图像
                from PIL import Image, ImageTk
                image = Image.open(image_path)
                        
                # 调整图像大小
                image.thumbnail((150, 150), Image.LANCZOS)
                        
                # 转换为Tkinter兼容的格式
                photo = ImageTk.PhotoImage(image)
                        
                # 创建标签显示图像
                img_label = tk.Label(img_container, image=photo, bg="white")
                img_label.__dict__['image'] = photo  # 保持引用以防止被垃圾回收
                img_label.pack(pady=(0, 5))
                        
                # 添加点击事件处理
                def on_image_click(event, idx=i, container=img_container, win_id=window_id):
                    # 获取选中状态集合
                    sel_indices = self.selected_image_indices.get(win_id, set())
                    if idx in sel_indices:
                        sel_indices.remove(idx)
                        container.config(bg="white", relief="raised")
                    else:
                        sel_indices.add(idx)
                        container.config(bg="lightblue", relief="ridge")
                    # 更新选中状态
                    self.selected_image_indices[win_id] = sel_indices
                        
                img_label.bind("<Button-1>", on_image_click)
                        
                # 显示图片信息
                basename = os.path.basename(image_path)
                ttk.Label(img_container, text=f"{basename}", font=("Arial", 8), wraplength=140).pack()
                ttk.Label(img_container, text=f"ID: {face_info.get('id', 'N/A')}", font=("Arial", 7), foreground="gray").pack()
                        
            except Exception as e:
                ttk.Label(img_container, text=f"无法加载\n{os.path.basename(image_path)}", 
                                 foreground="red", font=("Arial", 8)).pack(pady=20)
                
        # 设置列的权重
        for i in range(cols):
            scrollable_frame.columnconfigure(i, weight=1)
                
        # 在窗口完全显示后保持5列
        def adjust_columns():
            nonlocal cols
            cols = 5  # 始终保持5列
            rearrange_images()
                
        # 延迟调整列数，确保窗口已完全渲染
        parent_window.after(100, adjust_columns)
    
    def _display_single_image(self, parent_frame, image_path):
        """显示单张图片"""
        # 使用PIL加载和显示图像
        from PIL import Image, ImageTk
        
        # 加载图像
        image = Image.open(image_path)
        
        # 调整图像大小以适应窗口
        image.thumbnail((500, 300), Image.LANCZOS)
        
        # 转换为Tkinter兼容的格式
        photo = ImageTk.PhotoImage(image)
        
        # 创建标签显示图像
        image_label = ttk.Label(parent_frame, image=photo)
        image_label.__dict__['image'] = photo  # 保持引用以防止被垃圾回收
        image_label.pack(expand=True)
        
        # 显示文件信息
        ttk.Label(parent_frame, text=f"文件: {os.path.basename(image_path)}", font=("Arial", 9), foreground="gray").pack(pady=(5, 0))
        ttk.Label(parent_frame, text=f"路径: {image_path}", font=("Arial", 8), foreground="gray").pack()
    
    def _display_multiple_images(self, parent_frame, image_paths, name):
        """显示多张图片，带切换功能"""
        from PIL import Image, ImageTk
        
        # 当前显示的图片索引
        current_index = tk.IntVar(value=0)
        
        # 创建图片显示区域
        image_display_frame = ttk.Frame(parent_frame)
        image_display_frame.pack(fill="both", expand=True)
        
        # 图片标签
        image_label = ttk.Label(image_display_frame)
        image_label.pack(expand=True)
        
        # 图片信息标签
        info_label = ttk.Label(parent_frame, font=("Arial", 9), foreground="gray")
        info_label.pack(pady=(5, 0))
        
        # 导航按钮
        nav_frame = ttk.Frame(parent_frame)
        nav_frame.pack(pady=(5, 0))
        
        def update_display():
            """更新图片显示"""
            idx = current_index.get()
            if 0 <= idx < len(image_paths):
                image_path = image_paths[idx]
                
                # 加载图像
                image = Image.open(image_path)
                
                # 调整图像大小以适应窗口
                image.thumbnail((500, 300), Image.LANCZOS)
                
                # 转换为Tkinter兼容的格式
                photo = ImageTk.PhotoImage(image)
                
                # 更新图片显示
                image_label.configure(image=photo)
                image_label.__dict__['image'] = photo  # 保持引用以防止被垃圾回收
                
                # 更新信息显示
                info_label.configure(text=f"文件: {os.path.basename(image_path)} ({idx+1}/{len(image_paths)})")
        
        def next_image():
            """下一张图片"""
            idx = current_index.get()
            if idx < len(image_paths) - 1:
                current_index.set(idx + 1)
                update_display()
        
        def prev_image():
            """上一张图片"""
            idx = current_index.get()
            if idx > 0:
                current_index.set(idx - 1)
                update_display()
        
        # 创建导航按钮
        ttk.Button(nav_frame, text="上一张", command=prev_image).pack(side="left", padx=(0, 5))
        ttk.Button(nav_frame, text="下一张", command=next_image).pack(side="left", padx=(5, 0))
        
        # 显示第一张图片
        update_display()
    
   
    def _get_available_cameras(self):
        """检测可用的摄像头"""
        available_cameras = []
        try:
            # 检查OpenCV是否可用
            if cv2 is None:
                return available_cameras
                
            # 尝试打开前5个摄像头索引
            for i in range(5):
                cap = getattr(cv2, 'VideoCapture')(i)
                if cap.isOpened():
                    # 尝试读取一帧来确认摄像头是否真正可用
                    ret, frame = cap.read()
                    if ret:
                        available_cameras.append(i)
                cap.release()
        except Exception as e:
            print(f"检测摄像头时出错: {e}")
        return available_cameras
    
    def _select_camera(self, available_cameras, parent_window):
        """让用户选择摄像头"""
        # 创建选择对话框
        dialog = tk.Toplevel(parent_window)
        dialog.title("选择摄像头")
        dialog.geometry("300x200")
        dialog.transient(parent_window)
        dialog.grab_set()
        
        # 居中显示
        dialog.update_idletasks()
        x = parent_window.winfo_x() + (parent_window.winfo_width() // 2) - (300 // 2)
        y = parent_window.winfo_y() + (parent_window.winfo_height() // 2) - (200 // 2)
        dialog.geometry(f"300x200+{x}+{y}")
        
        ttk.Label(dialog, text="检测到多个摄像头，请选择一个:", font=("Arial", 10)).pack(pady=10)
        
        # 创建列表框显示可用摄像头
        list_frame = ttk.Frame(dialog)
        list_frame.pack(fill="both", expand=True, padx=20, pady=10)
        
        camera_listbox = tk.Listbox(list_frame, selectmode="single")
        camera_listbox.pack(side="left", fill="both", expand=True)
        
        scrollbar = ttk.Scrollbar(list_frame, orient="vertical", command=camera_listbox.yview)
        scrollbar.pack(side="right", fill="y")
        camera_listbox.configure(yscrollcommand=scrollbar.set)
        
        # 添加摄像头到列表
        for i, cam_index in enumerate(available_cameras):
            camera_listbox.insert("end", f"摄像头 {cam_index}")
        
        # 默认选择第一个
        if available_cameras:
            camera_listbox.selection_set(0)
        
        selected_index = None
        
        def on_select():
            nonlocal selected_index
            selection = camera_listbox.curselection()
            if selection:
                selected_index = available_cameras[selection[0]]
                dialog.destroy()
        
        def on_cancel():
            nonlocal selected_index
            selected_index = None
            dialog.destroy()
        
        # 按钮框架
        button_frame = ttk.Frame(dialog)
        button_frame.pack(pady=10)
        
        ttk.Button(button_frame, text="确定", command=on_select).pack(side="left", padx=5)
        ttk.Button(button_frame, text="取消", command=on_cancel).pack(side="left", padx=5)
        
        # 等待对话框关闭
        dialog.wait_window()
        
        return selected_index
    
    def on_face_selection_changed(self, event):
        """人脸库选择变化事件处理"""
        self.update_database_status()
    
    def _update_scrollregion(self, canvas):
        """更新画布的滚动区域"""
        bbox = canvas.bbox("all")
        if bbox:
            # 修复：将bbox元组解包为独立参数
            canvas.configure(scrollregion=(bbox[0], bbox[1], bbox[2], bbox[3]))
        else:
            # 如果没有内容，设置默认滚动区域
            canvas.configure(scrollregion=(0, 0, 100, 100))


def main():
    root = tk.Tk()
    app = FaceRecognitionGUI(root)
    root.mainloop()


if __name__ == "__main__":
    main()